Skip to content

Instantly share code, notes, and snippets.

@Loafer19
Last active March 23, 2024 16:53
Show Gist options
  • Save Loafer19/e65b6982ee40f6704318436e3b5ca6d4 to your computer and use it in GitHub Desktop.
Save Loafer19/e65b6982ee40f6704318436e3b5ca6d4 to your computer and use it in GitHub Desktop.
JWT => JSON Web Tokens => Advanced level

JSON Web Token

JWT - is a JSON-based access token standard.

It is typically used to transfer data for authentication in client-server applications.

Tokens are created by the server, signed with a secret key, and sent to the client, who then uses this token to verify his identity.

Flow (simplified)

Client part:

  • on login request:
    • sends credentials
    • receives tokens
  • on each request:
    • uses an access token
  • on refresh request:
    • sends refresh
    • receives tokens

Server part:

  • on login request:
    • signs the access key
    • stores the refresh token
    • return pair of tokens
  • on each request:
    • validates the access token
    • identifies user
  • on refresh request:
    • validates the current refresh
    • deletes the refresh token
    • returns new pair of tokens

Security

Since the "secret-key" is only revealed to the server, only it can issue new tokens with an authentic signature.

Users can't forge tokens and create a valid signature, as the token demands knowledge of the "secret-key".

These are generally passed in the "authorization" header at the time a user submits a request.

Advantages

  • Simplicity - easy to understand, use and implement
  • Compatibility - can be used with different languages, platforms, libraries, frameworks and tools
  • Cross-domain - can be used in different domains
  • Scalability - can be used with load balancers
  • Security - can be used with different encryption algorithms
  • Mobile - can be used in mobile applications
  • Stateless - no need to store tokens on the server?
  • Extensibility - can be used with different claims

Disadvantages

  • Size - the token is larger than the session id
  • Storage - the server must store the token?
  • Transfer - the token is sent with each request
  • Revocation - the token cannot be revoked
  • Rotation - the token cannot be rotated

Token Structure

Token string is made up of three components divided by a dot:

  • header that specifies the algorithm used to encrypt the contents of the token;
  • payload that contains "claims" (information the token securely transmits);
  • signature that can be used to verify the authenticity of the information.

The first two blocks are in base64 encoded JSON format.

The JWT standard defines several reserved names (iss, aud, exp, and others).

The signature can be generated using both symmetric and asymmetric encryption algorithms.

Example: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyIjp7ImlkIjo1LCJuYW1lIjoiUnVzbGFuIFBvcGVseXNoeW4iLCJlbWFpbCI6InNvbWVAbWFpbC5jb20ifSwianRpIjoiYWNjZXNzX3Rva2VuIiwiYXVkIjoiR2l0SHViIEdpc3QiLCJpc3MiOiJwb3BlbHlzaHluLnBwLnVhIiwiZXhwIjoxNjkxNzcyMjI2LjMwNDg3OH0.fYe5aCcrygBHywCvdV2dsLMRIatc8Fd6Ubjs4K6cxrc

Header:

{
  "typ": "JWT",
  "alg": "HS256"
}

Payload:

{
  "user": {
    "id": 5,
    "name": "Ruslan Popelyshyn",
    "email": "some@mail.com"
  },
  "jti": "access_token",
  "aud": "GitHub Gist",
  "iss": "popelyshyn.pp.ua",
  "exp": 1691772226.304878
}

Signature: fYe5aCcrygBHywCvdV2dsLMRIatc8Fd6Ubjs4K6cxrc

Tokens Details

Property Access Refresh
Purpose Access to resources Access to refresh
Usage Used for requests Used for refresh
Lifetime Short, usually 15 minutes Long, usually 30 days
Storage In state of app Cookie's or local storage
Security Not encrypted, but signed Encrypted and signed
Validation Validated by the server Validated by the server
Revocation Not revoked Revoked after refresh
Rotation Not rotated Rotated after refresh
Transfer Sent with each request Sent only on refresh
Payload Contains user data Contains device info

Login Process

During each login, a record is created with IP/Fingerprint and other meta information, the so-called refresh session.

  1. Client sends a request to an api endpoint: api/auth/login
  2. Server checks credentials
  3. Server creates a pair of tokens
    • Access token
    • Stores refresh session
  4. Sends a response to the client
    • Access token
    • Refresh token UUID
  5. Client stores tokens in storage

You should limit the number (usually 5) of refresh sessions per user.

This will allow you to limit the number of devices that can be logged in at the same time.

The easiest way is just to delete old sessions when creating a new one.

Refresh Process

  1. Before the request or by timeout, the client checks whether the lifetime of the access token has expired
  2. If the time has expired, a request is sent to an api endpoint: api/auth/refresh
  3. Server retrieves the "refresh session" by the UUID of the refresh token
    • Removes session from storage
    • Checks the current session for its lifetime
    • Compares fingerprints
  4. On failure throws an exception
  5. On success stores a new "refresh session"
  6. Sends access and refresh token uuid to the client

Logout Process

  1. Client sends a request to an api endpoint: api/auth/logout
  2. Server removes "refresh session" by the UUID of the refresh token
  3. Client removes tokens from storage
  4. Sends a response to the client

access_token dies at the end of its lifetime.

There is no need to ban, delete, or store the access token manually, this violates the entire essence of the access token.

Cookies?

For web applications, it is recommended to use cookies to store refresh tokens.

After login/refresh requests, the server sends a cookie with the UUID of the refresh token.

With parameters:

  • HttpOnly - the cookie is not available to JavaScript
  • Secure - the cookie is only transmitted over HTTPS
  • SameSite - the cookie is not sent in cross-site requests
  • Domain - the cookie is only sent to the specified domain
  • Path - the cookie is only sent to the specified path
  • Expires - the cookie is only sent until the specified date
  • Max-Age - the cookie is only sent for the specified number of seconds

If we use cookies, then we do not need to store the refresh token UUID in the client storage.

Stolen and ...

It is not an easy task to steal all the authorization data.

IP/Fingerprint - the server checks the IP/Fingerprint of the device

Old Refresh - if we receive a request with an old refresh token, we can logout all devices

Additional explanations

Useful information

The following are scenarios in which JWT may be helpful:

Authentication – This is the most prevalent scenario.

After the user has logged in, every following request will feature the JWT, letting the user access services, routes and resources allowed with the token.

Single Sign On (SSO) commonly uses JWT today, because of its minimal overhead and its ability to be smoothly employed across various domains.

Information Exchange – JWT is one of the most effective ways of securely exchanging data between parties.

For instance, a JWT may be signed using private/public key pairs to confirm the senders identity.

Furthermore, as the signature is attained using the payload and the header, it is possible to verify that the content has not been compromised.

Why store refresh session on the server?

If you do not store the refresh token in the database, there is a high probability that the tokens will be uncontrolled in the hands of attackers. To track them, we will have to create a blacklist and periodically clean it of overdue ones. Instead, we keep a limited list of white tokens for each user separately.

Fingerprint

For creating a fingerprint, you can use some sort of device information.

For example, you can use the following data:

  • User-Agent
  • Accept-Language
  • Screen resolution
  • Timezone
  • Platform
  • ...

You can sent client fingerprint in the request header.

Hash it with other data (ip) and store it in the refresh session.

*Be careful, on using ip address, because it can be changed, and you will have to re-login. Like on different wifi networks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment