JSON Web Token

This article contains information about JWT used for authentication at Virgil Cloud.

Authentication with JWT

To set up E3Kit you will need to generate tokens that are based on the JSON Web Token standard but with some Virgil modification.

In order to make calls to Virgil Services (for example, to publish user's Card on Virgil Cards Service), you need to have a JSON Web Token ("JWT") that contains the user's identity which is a string that uniquely identifies each user in your application. For this tutorial we'll assume that your user records have a unique ID assigned by the database, known as uid, and we will use that as their identity.

To show how and why JWT is actually used, we will use a simple 3 entity example (see the below diagram). The entities in this example are the User, the Application server, and the Virgil Cards Service. The authentication server will provide the JWT to the user. With the JWT, the user can then safely communicate with the Virgil Services.

JSON WEB TOKEN Generation

You can use any string value for the user identity as long as it's unique for each user. However, for security reasons, avoid using fields that contain any personally identifiable information such as name or email address, especially if your product needs to comply with regulations such as HIPAA and GDPR.

JWTs must be generated on the server side for several reasons:

  • each JWT must be signed with your Virgil App Key to prove it was issued by you,
  • the client side should not have access to your sensitive App Key and
  • you need to authenticate the user making the request to get their identity.

Each JWT grants a user access to the Virgil Cloud for a specific Virgil Application and has a limited lifetime that is configured by you. However, best practice is to generate a JWT for the shortest amount of time feasible for your application.

Backend samples

We've created a sample backend that demonstrates how you can set up your backend to generate the JWTs. To set up and run the sample backend locally, head over to your GitHub repo of choice:

Node.js | Golang | PHP | Java | Python and follow the instructions in README.

JWT structure

Each JWT consists of three parts: the header, the payload, and the signature.

// JWT Token structure
header.payload.signature

1. Header

The header contains information about how the JWT signature should be computed. The header is a JSON object in the following format:

{
  // the type of token. It MUST be "JWT"
  "typ": "JWT",
  // Signature algorithm. Currently supports only "VEDS512" (Virgil EdDSA SHA512)
  "alg": "VEDS512",
  // the content-type. It MUST be "virgil-jwt;v=1
  "cty": "virgil-jwt;v=1"
  // fingerprint of public key, that will be used to verify token. Equals to first 8 bytes of SHA512 of Public Key in DER format
  "kid": "70b447e321f3a0fd",
}

2. Payload

The payload is the data that‘s stored inside the JWT (this data is also referred to as the “claims” of the JWT). In our example, the Application server creates a JWT with the user information stored inside of it.

{
  // Issuer. Equals to virgil-<Application ID>
  "iss": "virgil-be00e10e4e1f4bf58f9b4dc85d79c77a",
  // Subject. Equals to identity-<Identity of owner>
  "sub": "identity-Alice",
  // Issued at. Utc timestamp that indicates when token was issued.
  "iat": 1518612517
  // Expires at. Utc timestamp that shows when token will expire. Tokens have a maximum age of 24 hours
  "exp": 1518698917,
}

Keep in mind that the size of the data will affect the overall size of the JWT. This generally isn’t an issue but having excessively large JWT may negatively affect performance and cause latency.

3. Signature

The signature section is a signed hash that serves to prove the authenticity of the token. It is compiled by hashing the JWT header and payload together with your App Key secret, which should only be known to your application and Virgil.

The signature is computed using the following pseudo code:

// Signature
signature = base64urlEncode(EdDSA+SHA512(SHA512(base64urlEncode(header) + "." + base64urlEncode(payload))))

base64url encodes the header and the payload that was created in steps 1 and 2. The algorithm then joins the resulting encoded strings together with a period (.) in between them. To get the JWT signature, the data string is hashed with the secret key using the hashing algorithm specified in the JWT header.

Then, using the joined encoded header and payload and applying the specified signature algorithm(HS256) on the data string with the secret key set as the string “secret”, we get the JWT Signature.

Now that we have created all three components, we can create the JWT. Remembering the header.payload.signature structure of the JWT, we simply need to combine the components with periods (.) separating them. We use the base64url encoded versions of the header and of the payload, and the signature.

Here is an example of JWT:

// JWT Token
eyJraWQiOiI3MGI0NDdlMzIxZjNhMGZkIiwidHlwIjoiSldUIiwiYWxnIjoiVkVEUzUxMiIsImN0eSI6InZpcmdpbC1qd3Q7dj0xIn0.eyJleHAiOjE1MTg2OTg5MTcsImlzcyI6InZpcmdpbC1iZTAwZTEwZTRlMWY0YmY1OGY5YjRkYzg1ZDc5Yzc3YSIsInN1YiI6ImlkZW50aXR5LUFsaWNlIiwiaWF0IjoxNTE4NjEyNTE3fQ.MFEwDQYJYIZIAWUDBAIDBQAEQP4Yo3yjmt8WWJ5mqs3Yrqc_VzG6nBtrW2KIjP-kxiIJL_7Wv0pqty7PDbDoGhkX8CJa6UOdyn3rBWRvMK7p7Ak

It is important to understand that the purpose of using JWT is NOT to hide or obscure data in any way. The reason why JWT is used is to prove that the sent data was actually created by an authentic source.

You can try creating your own JWT through your browser at jwt.io.