Logo

dev-resources.site

for different kinds of informations.

Learn OIDC - Part 2 - JWT

Published at
3/24/2024
Categories
java
jwt
oidc
diy
Author
coduinix
Categories
4 categories in total
java
open
jwt
open
oidc
open
diy
open
Author
8 person written this
coduinix
open
Learn OIDC - Part 2 - JWT

Introduction

For a project I was working on, I was looking for a lightweight OIDC Identity Provider to be used as a stub for our tests. I couldn’t find a lightweight one, so I tried to build one myself.

WARNING: The code shown in this series is for educational purposes only and is not meant to be used in production.

JWT

One of the important building blocks in OAuth and OIDC is the concept of JSON Web Token or JWT (often pronounced as ‘jot’). A JWT is basically a JSON Web Signature (see: Learn OIDC - Part 1 - JWS) with a set of claims, represented as a JSON object as payload. The advantage of JSON format for the claims is that it is fairly easy to parse and human-readable too.

Format

The most common representation of JWTs is the JWS Compact Serialization. The details of the JWT specification can be found in RFC 7519.

Claims

The claims in the JWT can be basically anything, but there is a set of Registered Claim Names. All these registered claim names are shortened to just 3 characters in order to provide a compact representation. Although all these Registered Claim Names are considered optional for the JWT specification, they are heavily used in OAuth and OIDC implementations.

So for our implementation we’re going to use (most of) these registered claim names as well.

  • iss - the principal that issued the claims, should be a string/uri
  • sub - the principal that is the subject of the claims, should be a string/uri
  • aud - an array of audiences that the JWT is intended for (if there’s just one audience, it could be a plain string/uri instead of an array)
  • exp - the expiration time identifies the expiration time after which the JWT must not be accepted for processing, should be a number representing seconds since the epoch
  • nbf - the not before identifies the time before which the JWT MUST NOT be accepted for processing, should be a number representing seconds since the epoch
  • iat - the issued at identifies the time at which the JWT was issued, should be a number representing seconds since the epoch
  • jti - the JWT ID provides a unique identifier for the JWT, should be a string and can be used to prevent detect token replays

Note that none of these are required from the perspective of the JWT spec. Those are just some registered, commonly used claims.

In the light of OIDC some of these claims are required for proper OIDC implementation with ID tokens, as we’ll see in a later post

Implementation

A straight-forward way to implement a method to generate a JWT could be to re-use the JWS implementation from part 1 and put some manually constructed JSON string as payload.

Something like:

var payload = """
        {
        "iss": "fake-oidc-idp",
        "sub": "johndoe",
        "aud": "app-under-test",
        "exp": 1711285526,
        "nbf": 1711285226,
        "iat": 1711285226,
        "jti": "88771636-b8ec-49b1-bf07-03117f989a7a"
        }""".replace("\n", "");

return Jws.compactRS256(payload.getBytes(StandardCharsets.UTF_8), privateKey, keyId);

Enter fullscreen mode Exit fullscreen mode

This will work for some hardcoded payload like this, but for implementing a lightweight OIDC stub we would need a bit more dynamic values.

Specifically the exp, nbf, and iat should be more dynamic. This could be done with some clever String.format setup, but it will get messy once we try to make the string fields dynamic. Because then we’d need all kinds of escaping to keep the JSON body properly formatted.

So the best move would be to include some battle-tested JSON library to help with this. In this series we’ll be using the well-known Jackson JSON library.

Assembling claims

The access token claims we will be creating for this example should be dynamic. For example the iat (issued at) should be set to the moment of generation. Next to that, tokens typically have some token lifetime which is expressed as the timestamp at which the token expires (exp claim). This also requires the current time plus the token lifetime.

Given that we already have fields or variables for privateKey, keyId, issuer (which is typically constant for the application) and tokenLifetimeInSeconds, the token generation method could look like:

private static final ObjectMapper MAPPER = new ObjectMapper();
static {
    MAPPER.configure(SerializationFeature.INDENT_OUTPUT, false);
    MAPPER.setSerializationInclusion(JsonInclude.Include.NON_NULL);
}

public String createJwt(String subject, String audience) throws GeneralSecurityException, JsonProcessingException {
    var nowSeconds = System.currentTimeMillis() / 1000;
    var claims = Map.of(
            "iss", issuer,
            "sub", subject,
            "aud", audience,
            "exp", nowSeconds + tokenLifetimeInSeconds,
            "nbf", nowSeconds,
            "iat", nowSeconds,
            "jti", UUID.randomUUID().toString()
    );
    var payload = MAPPER.writeValueAsBytes(claims);
    return Jws.compactRS256(payload, privateKey, keyId);
}

Enter fullscreen mode Exit fullscreen mode

This code first determines the current time in seconds since epoch and then assembles all the claims we want. For now, we use a random UUID as JWT ID (jti), this will be sufficient for all intents and purposes of the stub OIDC server we’re going to build.

As “API” for our JWT generation function we’ll require just these 2 parameters:

  • a subject containing some identifier of the user who is represented by the generated token
  • an audience (application which is supposed to process the token)

What’s next?

Since we now have some basic building blocks we could look into building the first steps of an OIDC login flow.

oidc Article's
30 articles in total
Favicon
Defending OAuth2: Advanced Tactics to Block Replay Attacks
Favicon
Understanding the Differences Between OAuth2 and OpenID Connect (OIDC)
Favicon
Demystifying Social Logins: How OAuth2 Powers Seamless Authentication
Favicon
OAuth2 vs. OpenID Connect: Understanding the Differences
Favicon
GitHub Action security hardening with OpenID (OIDC) Connect - "Password-Less"
Favicon
OIDC vs SAML: A Comprehensive Technical Comparison
Favicon
OIDC Prompt 101: A simple guide for developers
Favicon
How to create a WeCom App to enable WeCom Login for the Web app
Favicon
No More Passwords! OIDC Terraform Module Makes GCP-GitHub Authentication a Breeze
Favicon
Learn OIDC - Part 2 - JWT
Favicon
Oidc node mongodb adapter in normal functions
Favicon
Learn OIDC - Part 1 - JWS
Favicon
OpenVPN + SSO via OAUTH2
Favicon
Kubernetes Cluster as an OpenID Connect Identity Provider
Favicon
How To Configure Audience In Keycloak
Favicon
Single Sign-On (SSO) with Zoho in Vue3
Favicon
Demystifying OpenID Connect (OIDC) - The Key to Secure and Seamless Authentication
Favicon
Adding single sign-on to a Next.js app using OIDC
Favicon
Implementing OpenID Connect (OIDC) Authentication with Nuxt 3
Favicon
Connect GitHub Actions to Azure using OpenID Connect
Favicon
OpenID Connect authentication with Apache Kafka 3.1
Favicon
Writing Java library to build OAuth 2.0 Authorization Server / OpenID Connect Identity Provider
Favicon
SSO Building blocks - SAML, OAuth 2.0 and OpenID Connect
Favicon
ASP.NET: Autenticación OIDC Multi Tenant - Parte 2
Favicon
Keycloak 19.0.1 and Setting the id_token_hint
Favicon
OIDC Forever, IAM Credentials Never!
Favicon
Fortifying federated access to AWS via OIDC
Favicon
Understanding OAuth and OIDC: Introduction
Favicon
OAuth 2.0 and OpenID Connect Explained: Building Secure Authentication Systems
Favicon
Securely authenticate to Google Cloud from GitHub

Featured ones: