Documentation
AppRole example with VaultClient
This example mirrors the AppRole authentication flow with the original v1 client. It intentionally uses KV v1 at the secret mount so the code can show the legacy client's native write and read calls.
This example mirrors the AppRole authentication flow with the original v1
client. It intentionally uses KV v1 at the secret mount so the code can show
the legacy client’s native write and read calls.
What the workflow demonstrates
- Prepare Vault by enabling a KV v1 mount at
secret. - Write an application secret at
/secret/mysql/webapp. - Enable the
approleauth method. - Create a read-only
jenkinspolicy for that one secret path. - Register an AppRole with short-lived tokens.
- Generate
role_idandsecret_idcredentials. - Log in as an app with those AppRole credentials.
- Read the secret with the app token and assert the returned data.
This example uses the shared decorator-based runner and personas described in
examples/README.md.
Some AppRole operations use apiRequest() with a custom POST 200 command spec
inside the v1 personas because the original client does not expose dedicated
AppRole helpers.
Local Vault
From the repository root, start only the plain Vault service:
docker compose up -d vault
One Vault instance is enough. You do not need vault_tls or vault_mtls unless
you are specifically testing TLS.
For a fresh Vault state:
docker compose down --volumes --remove-orphans
docker compose up -d vault
Run
Install dependencies from the repository root:
npm install
Then run the example:
NANVC_VAULT_CLUSTER_ADDRESS=http://127.0.0.1:8200 npx tsx examples/app-role-v1/main.ts
The helper defaults to http://vault.local:8200. Use the environment variable
above when vault.local is not mapped on your machine.
Environment
For an existing Vault server, set:
export NANVC_VAULT_CLUSTER_ADDRESS=http://127.0.0.1:8200
export TEST_NANVC_VAULT_AUTH_TOKEN=<root-or-admin-token>
export TEST_NANVC_VAULT_UNSEAL_KEY=<unseal-key>
If the local Vault server is initialized by any example or integration helper, the helper writes a shared cache file under your OS temp directory with:
TEST_NANVC_VAULT_AUTH_TOKENTEST_NANVC_VAULT_UNSEAL_KEY
Shell-exported TEST_NANVC_* variables take precedence over cached values. If
Vault reports invalid token, the cached credentials probably belong to another
Vault instance or an older Docker volume. Export valid TEST_NANVC_* values, or
reset local Vault with the fresh-state commands above.
import assert from 'node:assert';
import type { AdminPersona } from '../common/personas/admin.js';
import type { AppPersona } from '../common/personas/app.js';
import type { OperatorPersona } from '../common/personas/operator.js';
import type { VaultResponseData, AppRoleCredentials } from '../common/personas/types.js';
import { expectSuccess } from '../common/personas/helpers.js';
import { example, runAs, runExample, workflow } from '../common/workflow/decorators.js';
const secretData = {
db_name: 'users',
username: 'admin',
password: 'passw0rd',
};
@example('AppRole authentication example')
class AppRoleExample {
private credentials!: AppRoleCredentials;
@workflow('operator', 'Prerequisites: ensure Vault is prepared and kv secrets engine is enabled')
@runAs({ persona: 'operator', version: 'v1' })
public async prepareVault(operator: OperatorPersona<'v1'>): Promise<void> {
await operator.ensureKvMountAvailable('credentials', 1);
}
@workflow('admin', 'Configure AppRole and create credentials')
@runAs({ persona: 'admin', version: 'v1' })
public async adminWorkflow(admin: AdminPersona<'v1'>): Promise<void> {
await expectSuccess(admin.vault.write('/credentials/mysql/webapp', secretData), 'Vault KV v1 write failed');
await admin.enableAppRoleAuth();
const jenkinsPolicy = [
"# Read-only permission on secrets stored at 'credentials/mysql/webapp'",
'path "credentials/mysql/webapp" {',
' capabilities = ["read"]',
'}',
].join('\n');
await admin.createPolicy('jenkins', jenkinsPolicy);
await admin.registerAppRole('jenkins', {
token_policies: ['jenkins'],
token_ttl: '20m',
token_max_ttl: '30m',
});
this.credentials = await admin.createAppRoleCredentials('jenkins');
}
@workflow('app', 'Log in with AppRole credentials and check policy permissions')
@runAs({ persona: 'app', version: 'v1' })
public async appWorkflow(app: AppPersona<'v1'>): Promise<void> {
await app.loginWithAppRole(this.credentials);
const secretResponse = await expectSuccess(
app.vault.read('/credentials/mysql/webapp'),
'Vault KV v1 read failed',
);
const secret = secretResponse.apiResponse as
| VaultResponseData<{
db_name: string;
username: string;
password: string;
}>
| undefined;
assert.deepStrictEqual(secret?.data, secretData, 'Retrieved secret data does not match the expected value');
}
}
runExample(AppRoleExample).catch((error) => {
console.error(error);
process.exitCode = 1;
});Source Files
- README source:
examples/app-role-v1/README.md - Runnable source:
examples/app-role-v1/main.ts
This page is generated from the example README. Edit the source README and run
npm run generate:docsto update it.