or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

core-utilities.mdcryptography.mdfunctional-programming.mdgenerators.mdhttp-clients.mdindex.mdjwt-utilities.mdserialization.mdspecialized-utilities.mdspring-integration.mdtext-processing.md

jwt-utilities.mddocs/

0

# JWT Utilities

1

2

JSON Web Token signing and encryption utilities with support for various algorithms, key management strategies, and integration with CAS authentication flows.

3

4

## JsonWebTokenSigner

5

6

JWT signing utilities for creating and validating signed tokens with configurable algorithms and key management.

7

8

```java { .api }

9

public class JsonWebTokenSigner {

10

11

// Supported algorithms (excludes 'none' for security)

12

public static final Set<String> ALGORITHM_ALL_EXCEPT_NONE = Set.of(

13

"HS256", "HS384", "HS512", // HMAC algorithms

14

"RS256", "RS384", "RS512", // RSA algorithms

15

"ES256", "ES384", "ES512", // ECDSA algorithms

16

"PS256", "PS384", "PS512" // RSA-PSS algorithms

17

);

18

19

// Constructors

20

public JsonWebTokenSigner(Key signingKey);

21

public JsonWebTokenSigner(Key signingKey, String algorithm);

22

public JsonWebTokenSigner(String secretKey, String algorithm);

23

24

// Signing operations

25

public byte[] sign(byte[] value);

26

public String sign(JwtClaims claims);

27

public String sign(JwtClaims claims, Map<String, Object> headers);

28

29

// Validation operations

30

public boolean verify(String token);

31

public JwtClaims parse(String token);

32

public JwtClaims parseAndVerify(String token);

33

34

// Configuration

35

public String getAlgorithm();

36

public Key getSigningKey();

37

}

38

```

39

40

### Usage Examples

41

42

**Basic JWT signing:**

43

```java

44

@Service

45

public class TokenService {

46

47

private final JsonWebTokenSigner jwtSigner;

48

49

public TokenService(@Value("${cas.jwt.signing-secret}") String secret) {

50

// HMAC-based signer with HS256

51

this.jwtSigner = new JsonWebTokenSigner(secret, "HS256");

52

}

53

54

public String createAccessToken(String userId, List<String> roles) {

55

// Create JWT claims

56

JwtClaims claims = new JwtClaims();

57

claims.setSubject(userId);

58

claims.setIssuer("cas-server");

59

claims.setAudience("cas-clients");

60

claims.setExpirationTimeMinutesInTheFuture(60); // 1 hour

61

claims.setIssuedAtToNow();

62

claims.setNotBeforeMinutesInThePast(1);

63

claims.setJwtId(UUID.randomUUID().toString());

64

65

// Add custom claims

66

claims.setClaim("roles", roles);

67

claims.setClaim("tokenType", "access");

68

69

// Sign and return token

70

return jwtSigner.sign(claims);

71

}

72

73

public String createRefreshToken(String userId) {

74

JwtClaims claims = new JwtClaims();

75

claims.setSubject(userId);

76

claims.setIssuer("cas-server");

77

claims.setExpirationTimeMinutesInTheFuture(10080); // 7 days

78

claims.setIssuedAtToNow();

79

claims.setClaim("tokenType", "refresh");

80

81

return jwtSigner.sign(claims);

82

}

83

84

public boolean validateToken(String token) {

85

try {

86

return jwtSigner.verify(token);

87

} catch (Exception e) {

88

log.warn("Token validation failed", e);

89

return false;

90

}

91

}

92

93

public Optional<TokenInfo> parseToken(String token) {

94

try {

95

JwtClaims claims = jwtSigner.parseAndVerify(token);

96

97

TokenInfo info = new TokenInfo();

98

info.setUserId(claims.getSubject());

99

info.setRoles((List<String>) claims.getClaimValue("roles"));

100

info.setTokenType((String) claims.getClaimValue("tokenType"));

101

info.setExpiresAt(claims.getExpirationTime().getValueInMillis());

102

103

return Optional.of(info);

104

105

} catch (Exception e) {

106

log.error("Failed to parse token", e);

107

return Optional.empty();

108

}

109

}

110

}

111

```

112

113

**RSA-based JWT signing:**

114

```java

115

@Configuration

116

public class JwtConfiguration {

117

118

@Bean

119

public JsonWebTokenSigner rsaJwtSigner() throws Exception {

120

// Load RSA private key for signing

121

PrivateKey privateKey = loadPrivateKey("classpath:jwt-private.pem");

122

return new JsonWebTokenSigner(privateKey, "RS256");

123

}

124

125

@Bean

126

public JsonWebTokenValidator rsaJwtValidator() throws Exception {

127

// Load RSA public key for validation

128

PublicKey publicKey = loadPublicKey("classpath:jwt-public.pem");

129

return new JsonWebTokenValidator(publicKey, "RS256");

130

}

131

132

private PrivateKey loadPrivateKey(String location) throws Exception {

133

Resource resource = resourceLoader.getResource(location);

134

return AbstractCipherExecutor.extractPrivateKeyFromResource(resource.getURI().toString());

135

}

136

137

private PublicKey loadPublicKey(String location) throws Exception {

138

Resource resource = resourceLoader.getResource(location);

139

return AbstractCipherExecutor.extractPublicKeyFromResource(resource.getURI().toString());

140

}

141

}

142

```

143

144

**Custom headers and claims:**

145

```java

146

@Service

147

public class AdvancedJwtService {

148

149

private final JsonWebTokenSigner jwtSigner;

150

151

public String createTokenWithCustomHeaders(String userId, String clientId) {

152

// Create claims

153

JwtClaims claims = new JwtClaims();

154

claims.setSubject(userId);

155

claims.setAudience(clientId);

156

claims.setExpirationTimeMinutesInTheFuture(30);

157

claims.setIssuedAtToNow();

158

159

// Custom claims for CAS context

160

claims.setClaim("authenticationMethod", "password");

161

claims.setClaim("serviceTicket", generateServiceTicket());

162

claims.setClaim("principalAttributes", getUserAttributes(userId));

163

164

// Custom headers

165

Map<String, Object> headers = new HashMap<>();

166

headers.put("typ", "JWT");

167

headers.put("alg", jwtSigner.getAlgorithm());

168

headers.put("kid", "cas-2024-01"); // Key ID for rotation

169

headers.put("cas-version", "7.2.4");

170

171

return jwtSigner.sign(claims, headers);

172

}

173

174

public String createServiceToken(String service, Map<String, Object> attributes) {

175

JwtClaims claims = new JwtClaims();

176

claims.setAudience(service);

177

claims.setIssuer("https://cas.example.com");

178

claims.setExpirationTimeMinutesInTheFuture(5); // Short-lived for services

179

claims.setIssuedAtToNow();

180

181

// Service-specific claims

182

claims.setClaim("serviceAttributes", attributes);

183

claims.setClaim("grantType", "service_ticket");

184

185

Map<String, Object> headers = Map.of(

186

"typ", "service+jwt",

187

"purpose", "service-validation"

188

);

189

190

return jwtSigner.sign(claims, headers);

191

}

192

}

193

```

194

195

## JsonWebTokenEncryptor

196

197

JWT encryption utilities for creating encrypted tokens with various key management strategies.

198

199

```java { .api }

200

public class JsonWebTokenEncryptor {

201

202

// Supported encryption algorithms (excludes 'none' for security)

203

public static final List<String> ALGORITHM_ALL_EXCEPT_NONE = List.of(

204

"RSA1_5", "RSA-OAEP", "RSA-OAEP-256", // RSA key encryption

205

"A128KW", "A192KW", "A256KW", // AES key wrapping

206

"dir", // Direct encryption

207

"A128GCMKW", "A192GCMKW", "A256GCMKW" // AES-GCM key wrapping

208

);

209

210

// Constructors

211

public JsonWebTokenEncryptor(Key encryptionKey);

212

public JsonWebTokenEncryptor(Key encryptionKey, String keyAlgorithm, String contentAlgorithm);

213

public JsonWebTokenEncryptor(String secretKey, String keyAlgorithm, String contentAlgorithm);

214

215

// Encryption operations

216

public String encrypt(Serializable payload);

217

public String encrypt(Object payload, Map<String, Object> headers);

218

public String encrypt(String plaintext);

219

220

// Decryption operations

221

public String decrypt(String encryptedToken);

222

public <T> T decrypt(String encryptedToken, Class<T> type);

223

public Map<String, Object> decryptToMap(String encryptedToken);

224

225

// Configuration

226

public String getKeyAlgorithm();

227

public String getContentAlgorithm();

228

public Key getEncryptionKey();

229

}

230

```

231

232

### Usage Examples

233

234

**Basic JWT encryption:**

235

```java

236

@Service

237

public class SecureTokenService {

238

239

private final JsonWebTokenEncryptor jwtEncryptor;

240

private final JsonWebTokenSigner jwtSigner;

241

242

public SecureTokenService(

243

@Value("${cas.jwt.encryption-secret}") String encryptionSecret,

244

@Value("${cas.jwt.signing-secret}") String signingSecret) {

245

246

// AES-GCM encryption

247

this.jwtEncryptor = new JsonWebTokenEncryptor(

248

encryptionSecret,

249

"dir", // Direct key agreement

250

"A256GCM" // AES-256-GCM content encryption

251

);

252

253

// HMAC signing

254

this.jwtSigner = new JsonWebTokenSigner(signingSecret, "HS256");

255

}

256

257

public String createSecureToken(SensitiveData data) {

258

// First encrypt the sensitive data

259

String encryptedData = jwtEncryptor.encrypt(data);

260

261

// Then sign the encrypted token

262

JwtClaims claims = new JwtClaims();

263

claims.setSubject(data.getUserId());

264

claims.setExpirationTimeMinutesInTheFuture(15);

265

claims.setIssuedAtToNow();

266

claims.setClaim("encryptedPayload", encryptedData);

267

268

return jwtSigner.sign(claims);

269

}

270

271

public Optional<SensitiveData> extractSecureData(String token) {

272

try {

273

// Verify and parse the signed token

274

JwtClaims claims = jwtSigner.parseAndVerify(token);

275

String encryptedPayload = (String) claims.getClaimValue("encryptedPayload");

276

277

// Decrypt the payload

278

SensitiveData data = jwtEncryptor.decrypt(encryptedPayload, SensitiveData.class);

279

return Optional.of(data);

280

281

} catch (Exception e) {

282

log.error("Failed to extract secure data from token", e);

283

return Optional.empty();

284

}

285

}

286

}

287

```

288

289

**RSA-based encryption with key rotation:**

290

```java

291

@Service

292

public class KeyRotationAwareJwtService {

293

294

private final Map<String, JsonWebTokenEncryptor> encryptors;

295

private final Map<String, JsonWebTokenSigner> signers;

296

private String currentKeyId;

297

298

public KeyRotationAwareJwtService() {

299

this.encryptors = new ConcurrentHashMap<>();

300

this.signers = new ConcurrentHashMap<>();

301

initializeKeys();

302

}

303

304

private void initializeKeys() {

305

try {

306

// Load current keys

307

String keyId = "2024-01";

308

PublicKey publicKey = loadPublicKey("jwt-public-" + keyId + ".pem");

309

PrivateKey privateKey = loadPrivateKey("jwt-private-" + keyId + ".pem");

310

311

// RSA encryption (recipient uses private key to decrypt)

312

encryptors.put(keyId, new JsonWebTokenEncryptor(publicKey, "RSA-OAEP", "A256GCM"));

313

314

// RSA signing (sender uses private key to sign)

315

signers.put(keyId, new JsonWebTokenSigner(privateKey, "RS256"));

316

317

this.currentKeyId = keyId;

318

319

} catch (Exception e) {

320

throw new RuntimeException("Failed to initialize JWT keys", e);

321

}

322

}

323

324

public String createRotationAwareToken(Object payload) {

325

// Encrypt with current public key

326

JsonWebTokenEncryptor encryptor = encryptors.get(currentKeyId);

327

328

Map<String, Object> headers = Map.of(

329

"kid", currentKeyId, // Key ID for rotation

330

"alg", "RSA-OAEP",

331

"enc", "A256GCM"

332

);

333

334

String encryptedToken = encryptor.encrypt(payload, headers);

335

336

// Sign with current private key

337

JwtClaims wrapperClaims = new JwtClaims();

338

wrapperClaims.setIssuedAtToNow();

339

wrapperClaims.setExpirationTimeMinutesInTheFuture(60);

340

wrapperClaims.setClaim("encryptedJwt", encryptedToken);

341

342

JsonWebTokenSigner signer = signers.get(currentKeyId);

343

Map<String, Object> signHeaders = Map.of("kid", currentKeyId);

344

345

return signer.sign(wrapperClaims, signHeaders);

346

}

347

348

public <T> Optional<T> extractPayload(String token, Class<T> type) {

349

try {

350

// Parse outer signed JWT to get key ID

351

String[] parts = token.split("\\.");

352

String headerJson = new String(Base64.getUrlDecoder().decode(parts[0]));

353

JsonNode headerNode = objectMapper.readTree(headerJson);

354

String keyId = headerNode.get("kid").asText();

355

356

// Verify signature with appropriate key

357

JsonWebTokenSigner signer = signers.get(keyId);

358

if (signer == null) {

359

log.error("Unknown key ID: {}", keyId);

360

return Optional.empty();

361

}

362

363

JwtClaims claims = signer.parseAndVerify(token);

364

String encryptedJwt = (String) claims.getClaimValue("encryptedJwt");

365

366

// Decrypt with appropriate key (need corresponding private key)

367

JsonWebTokenEncryptor encryptor = getDecryptorForKeyId(keyId);

368

T payload = encryptor.decrypt(encryptedJwt, type);

369

370

return Optional.of(payload);

371

372

} catch (Exception e) {

373

log.error("Failed to extract payload from rotation-aware token", e);

374

return Optional.empty();

375

}

376

}

377

}

378

```

379

380

## Integration with CAS Authentication

381

382

### CAS Service Token Creation

383

384

```java

385

@Component

386

public class CasJwtTokenService {

387

388

private final JsonWebTokenSigner signer;

389

private final JsonWebTokenEncryptor encryptor;

390

391

public String createServiceTicketJwt(Authentication authentication, Service service) {

392

// Create CAS-specific claims

393

JwtClaims claims = new JwtClaims();

394

claims.setSubject(authentication.getPrincipal().getId());

395

claims.setIssuer("https://cas.example.com");

396

claims.setAudience(service.getId());

397

claims.setExpirationTimeMinutesInTheFuture(5); // Short-lived service tickets

398

claims.setIssuedAtToNow();

399

claims.setJwtId(generateServiceTicketId());

400

401

// CAS authentication context

402

claims.setClaim("authenticationMethod", getAuthenticationMethod(authentication));

403

claims.setClaim("authenticationTime", authentication.getAuthenticationDate().toEpochMilli());

404

claims.setClaim("principalAttributes", authentication.getPrincipal().getAttributes());

405

claims.setClaim("serviceId", service.getId());

406

407

// Service-specific attributes

408

if (service instanceof RegisteredService registeredService) {

409

claims.setClaim("serviceName", registeredService.getName());

410

claims.setClaim("serviceDescription", registeredService.getDescription());

411

}

412

413

return signer.sign(claims);

414

}

415

416

public String createTicketGrantingTicketJwt(Authentication authentication) {

417

JwtClaims claims = new JwtClaims();

418

claims.setSubject(authentication.getPrincipal().getId());

419

claims.setIssuer("https://cas.example.com");

420

claims.setExpirationTimeMinutesInTheFuture(480); // 8 hours

421

claims.setIssuedAtToNow();

422

claims.setJwtId(generateTicketGrantingTicketId());

423

424

// TGT-specific claims

425

claims.setClaim("ticketType", "TicketGrantingTicket");

426

claims.setClaim("authenticationTime", authentication.getAuthenticationDate().toEpochMilli());

427

claims.setClaim("principalId", authentication.getPrincipal().getId());

428

claims.setClaim("credentialType", getCredentialType(authentication));

429

430

// Encrypt TGT for additional security

431

String signedToken = signer.sign(claims);

432

return encryptor.encrypt(signedToken);

433

}

434

435

public boolean validateServiceTicketJwt(String token, Service service) {

436

try {

437

JwtClaims claims = signer.parseAndVerify(token);

438

439

// Validate audience matches service

440

if (!service.getId().equals(claims.getAudience().get(0))) {

441

return false;

442

}

443

444

// Check expiration

445

if (claims.getExpirationTime().isBefore(NumericDate.now())) {

446

return false;

447

}

448

449

return true;

450

451

} catch (Exception e) {

452

log.error("Service ticket JWT validation failed", e);

453

return false;

454

}

455

}

456

}

457

```

458

459

### JWT-based Authentication Provider

460

461

```java

462

@Component

463

public class JwtAuthenticationProvider implements AuthenticationProvider {

464

465

private final JsonWebTokenSigner jwtSigner;

466

private final PrincipalResolver principalResolver;

467

468

@Override

469

public Authentication authenticate(Authentication authentication) throws AuthenticationException {

470

471

if (!(authentication instanceof JwtAuthenticationToken jwtAuth)) {

472

return null;

473

}

474

475

try {

476

String token = jwtAuth.getToken();

477

JwtClaims claims = jwtSigner.parseAndVerify(token);

478

479

// Extract principal information

480

String principalId = claims.getSubject();

481

Map<String, Object> attributes = (Map<String, Object>) claims.getClaimValue("principalAttributes");

482

483

// Create CAS principal

484

Principal principal = principalResolver.resolve(

485

new UsernamePasswordCredential(principalId, ""),

486

Optional.empty(),

487

Optional.empty(),

488

Optional.empty()

489

);

490

491

// Create authentication result

492

AuthenticationHandlerExecutionResult result = new DefaultAuthenticationHandlerExecutionResult(

493

this,

494

new BasicCredentialMetaData(new UsernamePasswordCredential(principalId, "")),

495

principal

496

);

497

498

return DefaultAuthenticationBuilder.newInstance()

499

.addCredential(new UsernamePasswordCredential(principalId, ""))

500

.addSuccess("jwtAuthenticationHandler", result)

501

.build();

502

503

} catch (Exception e) {

504

throw new AuthenticationException("JWT authentication failed", e);

505

}

506

}

507

508

@Override

509

public boolean supports(Class<?> authentication) {

510

return JwtAuthenticationToken.class.isAssignableFrom(authentication);

511

}

512

}

513

```

514

515

This JWT utilities library provides comprehensive support for secure token-based authentication in CAS environments, with proper key management, algorithm selection, and integration with CAS authentication flows.