JSON Web Tokens
JSON Web Token is an open standard that defines a way for securely transmitting information between parties as a JSON object. This information can be verified and trusted since it is signed using a shared secret (with the HS256
algorithm) or a public/private key pair (for example, RS256
).
Ktor handles JWTs passed in the Authorization
header using the Bearer
schema and allows you to:
verify the signature of a JSON web token;
perform additional validations on the JWT payload.
Add dependencies
To enable JWT
authentication, you need to include the ktor-auth
and ktor-auth-jwt
artifacts in the build script:
JWT authorization flow
The JWT authorization flow in Ktor might look as follows:
A client makes a
POST
request with the credentials to a specific authentication route in a server application. The example below shows an HTTP clientPOST
request with the credentials passed in JSON:POST http://localhost:8080/login Content-Type: application/json { "username": "jetbrains", "password": "foobar" }If the credentials are valid, a server generates a JSON web token and signs it with the specified algorithm. For example, this might be
HS256
with a specific shared secret orRS256
with a public/private key pair.A server sends a generated JWT to a client.
A client can now make a request to a protected resource with a JSON web token passed in the
Authorization
header using theBearer
schema.GET http://localhost:8080/hello Authorization: Bearer {{auth_token}}A server receives a request and performs the following validations:
Verifies a token's signature. Note that a verification way depends on the algorithm used to sign a token.
Perform additional validations on the JWT payload.
After validation, a server responds with the contents of a protected resource.
Install JWT
To install the jwt
authentication provider, call the jwt function inside the install
block:
You can optionally specify a provider name that can be used to authenticate a specified route.
Configure JWT
In this section, we'll see how to use JSON web tokens in a server Ktor application. We'll demonstrate two approaches to signing tokens since they require slightly different ways to verify tokens:
Using
HS256
with a specified shared secret.Using
RS256
with a public/private key pair.
You can find full runnable projects here: auth-jwt-hs256, auth-jwt-rs256.
Step 1: Configure JWT settings
To configure JWT-related settings, you can create a custom jwt
group in the application.conf configuration file. This file might look as follows:
You can access these settings in code in the following way:
Step 2: Generate a token
To generate a JSON web token, you can use JWTCreator.Builder. Code snippets below show how to do this for both HS256
and RS256
algorithms:
post("/login")
defines an authentication route for receivingPOST
requests.call.receive<User>()
receives user credentials sent as a JSON object and converts it to aUser
class object.JWT.create()
generates a token with the specified JWT settings, adds a custom claim with a received username, and signs a token with the specified algorithm:For
HS256
, a shared secret is used to sign a token.For
RS256
, a public/private key pair is used.
call.respond
sends a token to a client as a JSON object.
Step 3: Configure realm
The realm
property allows you to set the realm to be passed in WWW-Authenticate
header when accessing a protected route.
Step 4: Configure a token verifier
The verifier function allows you to verify a token format and its signature:
For
HS256
, you need to pass a JWTVerifier instance to verify a token.For
RS256
, you need to pass JwkProvider, which specifies a JWKS endpoint for accessing a public key used to verify a token. In our case, an issuer ishttp://0.0.0.0:8080
, so a JWKS endpoint address will behttp://0.0.0.0:8080/.well-known/jwks.json
.
Step 5: Validate JWT payload
The validate function allows you to perform additional validations on the JWT payload in the following way:
Check the
credential
parameter, which represents a JWTCredential object and contains the JWT payload. In the example below, the value of a customusername
claim is checked.In a case of successful authentication, return JWTPrincipal. If authentication fails, return
null.
Step 6: Define authorization scope
After configuring the jwt
provider, you can define the authorization for the different resources in our application using the authenticate
function. In a case of successful authentication, you can retrieve an authenticated JWTPrincipal inside a route handler using the call.principal function and get the JWT payload. In the example below, the value of a custom username
claim and a token expiration time are retrieved.