Token authentication in Db2, introduced in 11.5.4, lets a client connect with a token instead of a password. This is useful in SSO-style integrations and application flows where identity has already been established upstream.
This article is deliberately practical. Every step below shows the exact files to create, the commands to run, and the checks to perform. The lab is designed to be reproduced on a Linux machine with a Db2 instance.
Authentication in Db2
Before addressing token authentication, it is helpful to list the important security aspects for Db2:
- Authentication validates and establishes identity
- Authorization determines what is permitted under a given identity
- Connection setup is the handshake that carries both the credentials and the security policy
In a classic Db2 connection, the client provides a username and password, Db2 validates them, and the resulting Authorization ID becomes the identity for the session. Token authentication changes the first step: instead of a password, the client provides a signed token to prove its identity.
That difference has important operational impacts. The application no longer has to manage the password. Instead, Db2 validates a token issued and signed on another system and then maps one of the token claims to the Db2 authid used by the session.
The way authorizations are validated remains unchanged. Db2 continues to apply privileges, roles, and database and instance administrative authorities to the identity that was established.
JWT
A JSON Web Token (JWT) is a compact way to carry claims about an identity and sign them so the receiver can trust the contents.
The JWT has three parts:
- a header, which describes the signing algorithm
- a payload, which carries claims such as issuer, subject, and expiration
- a signature, which proves the token was signed by a trusted entity
For this lab, the important claims are:
iss, which identifies the issuerusername, which we map to the Db2 authidiat, which tells you when the token was createdexp, which tells you when the token stops being valid
Db2 only needs enough of the JWT structure to trust the issuer, extract the mapped claim, and reject expired or malformed tokens. That is why the rest of the article uses a simple signed JWT instead of a full identity platform.
What this lab builds
By the end of the lab you will have:
- a local key pair for signing JWTs
- a PKCS#12 keystore containing the issuer certificate for Db2 validation
- a
db2token.cfgfile that trusts the issuer and maps a claim to the Db2 authid - Db2 configured to accept token-based server connections
- a database that accepts a token-based CLP connection
- a JDBC example that uses the same token
- a repeatable failure test for invalid or expired tokens
Prerequisites
Use a Db2 server at version 11.5.4 or later.
You also need:
- shell access to the Db2 instance owner account
opensslgsk8capicmd_64- the Db2 CLP
- a Java runtime and the Db2 JDBC driver if you want to run the Java example
1. Create the issuer key pair
The issuer is the system that signs the JWT. For the lab we keep it local so the flow is easy to reproduce.
Log in as the Db2 instance owner, create a working directory, and generate a 3072-bit RSA key:
mkdir -p "$HOME/db2-token-lab"
cd "$HOME/db2-token-lab"
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:3072 -out jwt-issuer.keyCreate a self-signed certificate from that key:
openssl req -new -x509 \
-key jwt-issuer.key \
-out jwt-issuer.crt \
-days 365 \
-subj "/CN=PYXIS-ISSUER/O=Pyxis/L=Lisbon/C=PT"The certificate subject is not the JWT issuer string. It is simply the certificate identity that Db2 will trust.
2. Create the keystore Db2 will use
Db2 needs a local PKCS#12 keystore containing the issuer certificate.
Create the keystore:
gsk8capicmd_64 -keydb -create \
-db "$HOME/db2-token-lab/jwtkeys.p12" \
-pw "Db2Token123!" \
-stashImport the issuer certificate into that keystore with a label that we will reference later:
gsk8capicmd_64 -cert -add \
-db "$HOME/db2-token-lab/jwtkeys.p12" \
-pw "Db2Token123!" \
-label db2jwt \
-file "$HOME/db2-token-lab/jwt-issuer.crt" \
-format asciiCheck the label if you want to confirm it was stored correctly:
gsk8capicmd_64 -cert -list \
-db "$HOME/db2-token-lab/jwtkeys.p12" \
-pw "Db2Token123!"Db2 will use this keystore to validate the JWT signature.
3. Create db2token.cfg
Now, create the token configuration file in the instance config directory:
cat > "$HOME/sqllib/cfg/db2token.cfg" <<EOF
VERSION=1
TOKEN_TYPES_SUPPORTED=JWT
JWT_KEYDB=$HOME/db2-token-lab/jwtkeys.p12
JWT_IDP_ISSUER=PYXIS-ISSUER
JWT_IDP_AUTHID_CLAIM=username
JWT_IDP_RSA_CERTIFICATE_LABEL=db2jwt
EOF
chmod 600 "$HOME/sqllib/cfg/db2token.cfg"What each line does:
VERSION=1declares the configuration file formatTOKEN_TYPES_SUPPORTED=JWTtells Db2 that JWT tokens are allowedJWT_KEYDBpoints to the local keystoreJWT_IDP_ISSUERmust match the JWTissclaim exactlyJWT_IDP_AUTHID_CLAIMtells Db2 which claim contains the authidJWT_IDP_RSA_CERTIFICATE_LABELidentifies the certificate in the keystore
Verify the file:
cat "$HOME/sqllib/cfg/db2token.cfg"4. Enable token authentication on the server
If the instance is not already using TCP/IP, enable the protocol now by configuring the DB2COMM Registry variable:
db2set DB2COMM=TCPIPSet the server connection authentication mode to token-based encryption:
db2 update dbm cfg using srvcon_auth SERVER_ENCRYPT_TOKENRestart the instance so the new configuration is applied:
db2stop force
db2startConfirm the parameter after restart:
db2 get dbm cfg | egrep -i "svcename|srvcon_auth"Determine the actual TCP port that Db2 is listening on. We will store the value in the DB2_PORT environment variable:
SVCENAME=$(db2 get dbm cfg | awk -F= '/TCP\/IP Service name/ {gsub(/[[:space:]]/, "", $2); print $2}')
if [[ "$SVCENAME" =~ ^[0-9]+$ ]]; then
DB2_PORT="$SVCENAME"
else
DB2_PORT=$(awk -v svc="$SVCENAME" '$1 == svc && $2 ~ /tcp/ {split($2, a, "/"); print a[1]; exit}' /etc/services)
fi
echo "$DB2_PORT"
ss -ltn | grep "$DB2_PORT"If DB2_PORT comes back empty, the service name from svcename is not mapped in /etc/services and JDBC will still not know which port to use.
5. Create a test database
Create a database for the demo:
db2 create database TOKDBConnect once to confirm the database is available and grant connect to the DEMO1 user:
db2 connect to TOKDB
db2 "grant connect on database to user DEMO1"
db2 connect reset
db2 terminate6. Generate a signed JWT
The token needs to contain at least:
issmatching the issuer name indb2token.cfg- a claim that maps to the authid
- an issued-at time
- an expiry time
Save the following script as make-jwt.sh in the lab directory:
#!/usr/bin/env bash
set -euo pipefail
if [ "$#" -lt 4 ]; then
echo "Usage: $0 <private-key> <issuer> <authid> <ttl-seconds>" >&2
exit 1
fi
key_file="$1"
issuer="$2"
authid="$3"
ttl_seconds="$4"
now=$(date +%s)
exp=$((now + ttl_seconds))
header='{"alg":"RS256","typ":"JWT"}'
payload=$(printf '{"username":"%s","sub":"%s","iss":"%s","iat":%s,"exp":%s}' \
"$authid" "$authid" "$issuer" "$now" "$exp")
b64url() {
openssl base64 -A | tr '+/' '-_' | tr -d '='
}
header_b64=$(printf '%s' "$header" | b64url)
payload_b64=$(printf '%s' "$payload" | b64url)
unsigned_token="${header_b64}.${payload_b64}"
signature_b64=$(printf '%s' "$unsigned_token" \
| openssl dgst -sha256 -sign "$key_file" -binary \
| b64url)
printf '%s.%s\n' "$unsigned_token" "$signature_b64"Make it executable:
chmod +x make-jwt.shGenerate a token for the DEMO1 authid with a one-hour lifetime:
./make-jwt.sh "$HOME/db2-token-lab/jwt-issuer.key" PYXIS-ISSUER DEMO1 3600 > token.jwtInspect the token file:
TOKEN=$(cat token.jwt)
base64 -d <<< $(echo $TOKEN | cut -d'.' -f1 | tr '_-' '/+')
base64 -d <<< $(echo $TOKEN | cut -d'.' -f2 | tr '_-' '/+')The username claim is important because db2token.cfg maps that claim to the Db2 authorization ID.
7. Connect from the Db2 CLP
Use the token in an explicit CLP connection:
db2 connect to TOKDB accesstoken "$(cat token.jwt)" accesstokentype jwtIf the connection succeeds, validate the mapped identity:
db2 "values current user"You should see the token-mapped authid in uppercase, which in this demo is DEMO1.
Disconnect and end the CLP session:
db2 connect reset
db2 terminate8. Connect from JDBC
The JDBC demo shows the token flow from a Java application.
Save the following code as TokenAuthDemo.java:
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import com.ibm.db2.jcc.DB2BaseDataSource;
import com.ibm.db2.jcc.DB2SimpleDataSource;
public class TokenAuthDemo {
public static void main(String[] args) throws Exception {
if (args.length < 3) {
throw new IllegalArgumentException("Usage: TokenAuthDemo <jwt> <host> <port>");
}
String token = args[0];
String host = args[1];
int port = Integer.parseInt(args[2]);
DB2SimpleDataSource ds = new DB2SimpleDataSource();
ds.setDriverType(4);
ds.setServerName(host);
ds.setPortNumber(port);
ds.setDatabaseName("TOKDB");
ds.setLoginTimeout(15);
ds.setSecurityMechanism(DB2BaseDataSource.TOKEN_SECURITY);
ds.setAccessToken(token);
ds.setAccessTokenType("JWT");
try (Connection conn = ds.getConnection();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("VALUES CURRENT USER")) {
while (rs.next()) {
System.out.println("CURRENT USER = " + rs.getString(1));
}
}
}
}Compile with the Db2 JDBC driver on the classpath:
javac -cp "$HOME/sqllib/java/db2jcc4.jar" TokenAuthDemo.javaIf you see errors such as package com.ibm.db2.jcc does not exist, your classpath is not pointing at the actual Db2 JDBC driver jar. On a typical Db2 server install, db2jcc4.jar is available under $HOME/sqllib/java/.
Run it with the same token:
java -cp ".:$HOME/sqllib/java/db2jcc4.jar" TokenAuthDemo "$(cat token.jwt)" localhost "$DB2_PORT"If the setup is correct, the program prints the token-mapped user.
The example sets a 15-second login timeout so it fails instead of hanging indefinitely.
If you get Connection refused, Db2 is not listening on the host/port used by JDBC. Check the svcename value, confirm the instance was restarted, resolve the real port, and validate with ss -ltn that the listener is really up.
9. Test with various failure scenarios
We now test with various failure scenarios.
Wrong issuer
Create a token with a different issuer string:
./make-jwt.sh "$HOME/db2-token-lab/jwt-issuer.key" BAD-ISSUER DEMO1 3600 > bad-issuer.jwt
db2 connect to TOKDB accesstoken "$(cat bad-issuer.jwt)" accesstokentype jwtThis should fail because BAD-ISSUER does not match JWT_IDP_ISSUER.
Expired token
Create a token with a negative lifetime:
./make-jwt.sh "$HOME/db2-token-lab/jwt-issuer.key" PYXIS-ISSUER DEMO1 -60 > expired.jwt
db2 connect to TOKDB accesstoken "$(cat expired.jwt)" accesstokentype jwtThis should fail because the token has already expired.
Wrong authid claim
Create a token for another user and try to connect to the database:
./make-jwt.sh "$HOME/db2-token-lab/jwt-issuer.key" PYXIS-ISSUER OTHERUSER 3600 > wrong-user.jwt
db2 connect to TOKDB accesstoken "$(cat wrong-user.jwt)" accesstokentype jwtThe connection fails. It can only succeed if that authid is acceptable in your environment. Otherwise, this is a clear way to show that claim-to-authid mapping matters.
Troubleshooting
If the connection does not work, check first whether:
db2token.cfgis in the instance config directory- the
issclaim matchesJWT_IDP_ISSUER - the token is signed with the same key as the trusted certificate
- the token has not expired
- Db2 is listening on the expected TCP/IP port
srvcon_authis set toSERVER_ENCRYPT_TOKEN- the instance was restarted after the changes
If JDBC fails while CLP works, check:
- the driver version
- the
TOKEN_SECURITYsetting - the
accessTokenTypevalue
Key takeaways
- Db2 token authentication is available starting with 11.5.4.
- The server uses
db2token.cfgto trust the issuer and map claims to authids. - CLP and JDBC can both authenticate with a JWT.
Useful IBM documentation
For deeper reading, the most useful IBM references for this topic are: