#!/usr/bin/env tsx import KcAdminClient from '@keycloak/keycloak-admin-client'; import invariant from 'tiny-invariant'; const main = async () => { const KEYCLOAK_HOST = process.env.KEYCLOAK_HOST; const KEYCLOAK_ADMIN_USER = process.env.KEYCLOAK_ADMIN_USER; const KEYCLOAK_ADMIN_PASSWORD = process.env.KEYCLOAK_ADMIN_PASSWORD; const realm = process.env.KEYCLOAK_REALM; const scopeName = process.env.SCOPE_NAME; const description = process.env.DESCRIPTION || ''; invariant(KEYCLOAK_HOST, 'KEYCLOAK_HOST environment variable is required'); invariant(KEYCLOAK_ADMIN_USER, 'KEYCLOAK_ADMIN_USER environment variable is required'); invariant(KEYCLOAK_ADMIN_PASSWORD, 'KEYCLOAK_ADMIN_PASSWORD environment variable is required'); invariant(realm, 'KEYCLOAK_REALM environment variable is required'); invariant(scopeName, 'SCOPE_NAME environment variable is required'); const kcAdminClient = new KcAdminClient({ baseUrl: `https://${KEYCLOAK_HOST}`, realmName: 'master', }); try { await kcAdminClient.auth({ username: KEYCLOAK_ADMIN_USER, password: KEYCLOAK_ADMIN_PASSWORD, grantType: 'password', clientId: 'admin-cli', }); console.log('Authentication successful.'); // Set target realm kcAdminClient.setConfig({ realmName: realm, }); // Check if scope already exists const existingScopes = await kcAdminClient.clientScopes.find(); const existingScope = existingScopes.find(scope => scope.name === scopeName); if (existingScope) { console.log(`Client scope '${scopeName}' already exists.`); return; } // Create client scope const result = await kcAdminClient.clientScopes.create({ name: scopeName, description: description || `${scopeName} scope`, protocol: 'openid-connect', includeInTokenScope: true, }); console.log(`Client scope '${scopeName}' created successfully with ID: ${result.id}`); } catch (error) { console.error('Error creating client scope:', error); process.exit(1); } }; main();