Skip to content

Commit

Permalink
feat(auth): add EntraId integration tests
Browse files Browse the repository at this point in the history
- Add integration tests for token renewal and re-authentication flows
- Update credentials provider to use uniqueId as username instead of account username
- Add test utilities for loading Redis endpoint configurations
- Split TypeScript configs into separate files for samples and integration tests
  • Loading branch information
bobymicroby committed Jan 10, 2025
1 parent ac972bd commit 3af6f10
Show file tree
Hide file tree
Showing 7 changed files with 118 additions and 6 deletions.
78 changes: 78 additions & 0 deletions packages/entraid/integration-tests/entraid-integration.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { BasicAuth } from '@redis/authx';
import { createClient } from '@redis/client';
import { EntraIdCredentialsProviderFactory } from '../lib/entra-id-credentials-provider-factory';
import { strict as assert } from 'node:assert';
import { spy } from 'sinon';
import { randomUUID } from 'crypto';
import { loadFromJson } from '@redis/test-utils/lib/cae-client-testing'

describe('EntraID Integration Tests', () => {

it('should automatically re-authenticate when token is renewed', async () => {

if (!process.env.REDIS_ENDPOINTS_CONFIG_PATH) {
throw new Error('REDIS_ENDPOINTS_CONFIG_PATH environment variable must be set');
}
if (!process.env.AZURE_CLIENT_ID) {
throw new Error('AZURE_CLIENT_ID environment variable must be set');
}
if (!process.env.AZURE_TENANT_ID) {
throw new Error('AZURE_TENANT_ID environment variable must be set');
}
if (!process.env.AZURE_CLIENT_SECRET) {
throw new Error('AZURE_CLIENT_SECRET environment variable must be set');
}

const endpoints = loadFromJson(process.env.REDIS_ENDPOINTS_CONFIG_PATH)
const clientId = process.env.AZURE_CLIENT_ID;
const tenantId = process.env.AZURE_TENANT_ID;
const clientSecret = process.env.AZURE_CLIENT_SECRET;

const entraidCredentialsProvider = EntraIdCredentialsProviderFactory.createForClientCredentials({
clientId: clientId,
clientSecret: clientSecret,
authorityConfig: { type: 'multi-tenant', tenantId: tenantId },
tokenManagerConfig: {
expirationRefreshRatio: 0.001
}
});

const client = createClient({
url: endpoints['standalone-entraid-acl'].endpoints[0],
credentialsProvider: entraidCredentialsProvider
});

const reAuthSpy = spy(client, <any>'reAuthenticate');

try {
await client.connect();

const startTime = Date.now();
while (Date.now() - startTime < 200) {
const key = randomUUID();
await client.set(key, 'value');
const value = await client.get(key);
assert.equal(value, 'value');
await client.del(key);
}

assert(reAuthSpy.callCount >= 1, `reAuthenticate should have been called at least once, but was called ${reAuthSpy.callCount} times`);

const uniqueCredentials = new Set(
reAuthSpy.getCalls().map(call => {
const creds = call.args[0] as BasicAuth;
return `${creds.username}:${creds.password}`;
})
);

assert.equal(
uniqueCredentials.size,
reAuthSpy.callCount,
`Expected ${reAuthSpy.callCount} different credentials, but got ${uniqueCredentials.size} unique credentials`
);

} finally {
client.destroy();
}
});
});
2 changes: 1 addition & 1 deletion packages/entraid/lib/entraid-credentials-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export class EntraidCredentialsProvider implements StreamingCredentialsProvider
this.onReAuthenticationError = options.onReAuthenticationError ??
((error) => console.error('ReAuthenticationError', error));
this.credentialsMapper = options.credentialsMapper ?? ((token) => ({
username: token.account?.username ?? undefined,
username: token.uniqueId,
password: token.accessToken
}));

Expand Down
3 changes: 2 additions & 1 deletion packages/entraid/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
"scripts": {
"clean": "rimraf dist",
"build": "npm run clean && tsc",
"start:auth-pkce": "npm run build && node dist/samples/auth-code-pkce/index.js",
"start:auth-pkce": "tsx --tsconfig tsconfig.samples.json ./samples/auth-code-pkce/index.ts",
"test-integration": "mocha -r tsx --tsconfig tsconfig.integration-tests.json './integration-tests/**/*.spec.ts'",
"test": "nyc -r text-summary -r lcov mocha -r tsx './lib/**/*.spec.ts'"
},
"dependencies": {
Expand Down
10 changes: 10 additions & 0 deletions packages/entraid/tsconfig.integration-tests.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"extends": "./tsconfig.json",
"include": [
"./integration-tests/**/*.ts",
"./lib/**/*.ts"
],
"compilerOptions": {
"noEmit": true
},
}
5 changes: 1 addition & 4 deletions packages/entraid/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,14 @@
"outDir": "./dist"
},
"include": [
"./samples/**/*.ts",
"./lib/**/*.ts"
],
"exclude": [
"./lib/test-utils.ts",
"./lib/**/*.spec.ts",
"./lib/sentinel/test-util.ts"
"./lib/test-util.ts",
],
"typedocOptions": {
"entryPoints": [
"./index.ts",
"./lib"
],
"entryPointStrategy": "expand",
Expand Down
10 changes: 10 additions & 0 deletions packages/entraid/tsconfig.samples.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"extends": "./tsconfig.json",
"include": [
"./samples/**/*.ts",
"./lib/**/*.ts"
],
"compilerOptions": {
"noEmit": true
}
}
16 changes: 16 additions & 0 deletions packages/test-utils/lib/cae-client-testing.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
interface RawRedisEndpoint {
username?: string;
password?: string;
tls: boolean;
endpoints: string[];
}

type RedisEndpointsConfig = Record<string, RawRedisEndpoint>;

export function loadFromJson(jsonString: string): RedisEndpointsConfig {
try {
return JSON.parse(jsonString) as RedisEndpointsConfig;
} catch (error) {
throw new Error(`Invalid JSON configuration: ${error}`);
}
}

0 comments on commit 3af6f10

Please sign in to comment.