Identity Provider (IdP) x Service Provider (SP)

JS
S
JavaScript

The Identity Provider (IdP): This entity is responsible for authenticating a user and then "issuing" a signed JWT. It holds the private key. The Service Provider (SP): This is our simulated service. It needs to verify incoming JWTs to grant access. It holds the corresponding public key. Prepare the project (Node.js): ```sh mkdir jwt-simulation cd jwt-simulation npm init -y npm install jsonwebtoken ```

1// simulateAuth.js
2// =====================
3// Import necessary libraries
4const jwt = require('jsonwebtoken');
5const crypto = require('crypto');
6
7// --- 1. KEY GENERATION ---
8// In a real-world application, you would generate this key pair once and store it securely.
9// The private key must be kept secret by the Identity Provider.
10// The public key is shared with the Service Provider (e.g., uploaded to AWS OpenSearch).
11console.log('🔑 Generating RSA Public/Private Key Pair...');
12const { privateKey, publicKey } = crypto.generateKeyPairSync('rsa', {
13  modulusLength: 2048, // Standard, secure length
14  publicKeyEncoding: {
15    type: 'spki',       // Standard for public keys
16    format: 'pem'
17  },
18  privateKeyEncoding: {
19    type: 'pkcs8',      // Standard for private keys
20    format: 'pem'
21  }
22});
23
24console.log('✅ Key pair generated successfully.\n');
25// You can uncomment the lines below to see what the keys look like.
26// console.log('--- Public Key ---');
27// console.log(publicKey);
28// console.log('--- Private Key ---');
29// console.log(privateKey);
30
31
32// --- 2. IDENTITY PROVIDER (IdP) SIMULATION ---
33// This function simulates the IdP creating and signing a JWT for an authenticated user.
34function issueJwtForUser(userId, roles) {
35  console.log(`\n--- Identity Provider (IdP) ---`);
36  console.log(`👤 Authenticated user: ${userId}`);
37  console.log(`📜 User roles: ${roles.join(', ')}`);
38
39  // The payload contains the "claims" about the user.
40  // 'sub' (subject) is a standard claim for the user's ID.
41  // 'exp' (expiration time) is a crucial security feature.
42  const payload = {
43    sub: userId,
44    roles: roles, // Custom claim for our application
45    iss: 'MyAwesomeIdP' // 'iss' (issuer) claim
46  };
47
48  // Sign the token using the private key.
49  // We specify the RS256 algorithm, which corresponds to RSA signing.
50  const signOptions = {
51    algorithm: 'RS256',
52    expiresIn: '1h' // Token will be valid for 1 hour
53  };
54
55  console.log('✍️  Signing a new JWT with the PRIVATE key...');
56  const token = jwt.sign(payload, privateKey, signOptions);
57
58  console.log('✅ JWT issued successfully.');
59  return token;
60}
61
62
63// --- 3. SERVICE PROVIDER (SP) SIMULATION ---
64// This function simulates our service (like OpenSearch) verifying the token and authorizing access.
65function verifyAndAuthorizeRequest(token, servicePublicKey) {
66  console.log(`\n--- Service Provider (SP) ---`);
67  console.log(`➡️  Received a request with a JWT.`);
68
69  try {
70    // The SP uses the PUBLIC key to verify the token's signature.
71    // If the token is invalid, expired, or the signature doesn't match, this will throw an error.
72    const verificationOptions = {
73      algorithms: ['RS256'], // Must match the signing algorithm
74      issuer: 'MyAwesomeIdP' // Optional: verify the issuer
75    };
76
77    console.log('🔎 Verifying JWT signature with the PUBLIC key...');
78    const decodedPayload = jwt.verify(token, servicePublicKey, verificationOptions);
79    
80    console.log('✅ Signature is valid!');
81    console.log('👤 Decoded User ID (sub):', decodedPayload.sub);
82    console.log('📜 Decoded Roles:', decodedPayload.roles);
83
84    // --- Authorization Step ---
85    // Now that we trust the token, we can use its contents to make an authorization decision.
86    if (decodedPayload.roles && decodedPayload.roles.includes('admin')) {
87      console.log('👑 Access Granted: User is an admin. Performing privileged action...');
88    } else if (decodedPayload.roles && decodedPayload.roles.includes('reader')) {
89      console.log('📖 Access Granted: User is a reader. Allowing read-only access...');
90    } else {
91      console.log('🚫 Access Denied: User does not have sufficient roles.');
92    }
93    
94  } catch (error) {
95    console.error('❌ JWT Verification Failed!', error.message);
96  }
97}
98
99// --- 4. RUN THE SIMULATION ---
100
101// A) Simulate a user with 'admin' rights logging in.
102const adminUser = { id: 'user-123', roles: ['reader', 'admin'] };
103const adminToken = issueJwtForUser(adminUser.id, adminUser.roles);
104console.log('\nGenerated JWT for admin:\n', adminToken);
105verifyAndAuthorizeRequest(adminToken, publicKey);
106
107console.log('\n==================================================\n');
108
109// B) Simulate a user with only 'reader' rights.
110const readerUser = { id: 'user-456', roles: ['reader'] };
111const readerToken = issueJwtForUser(readerUser.id, readerUser.roles);
112console.log('\nGenerated JWT for reader:\n', readerToken);
113verifyAndAuthorizeRequest(readerToken, publicKey);
114
115console.log('\n==================================================\n');
116
117// C) FAILED SIMULATION: Tampering with the token
118console.log(`\n--- Tampering Simulation ---`);
119const tamperedToken = adminToken + 'tamper'; // Add garbage to the end of the token
120verifyAndAuthorizeRequest(tamperedToken, publicKey);
121
122// ** [Coauthored with AI] ** 

Created on 6/19/2025