Skip to content

Commit e2f6c66

Browse files
mrdailey99claude
andcommitted
feat(auth): surface request-access URL for users without accounts
Add REQUEST_ACCESS_URL constant and display it at every auth dead-end: - sf provar auth login: catch 401 from exchange and show request URL - sf provar auth status: "no key configured" block includes request URL - MCP ONBOARDING_MESSAGE and AUTH_WARNING include request URL - QualityHubAuthError from /auth/exchange includes request URL - docs/mcp.md: "Don't have an account?" in Authentication section - README.md: "Get Access" badge + inline link in MCP section - messages/sf.provar.auth.login.md and status.md updated Note: provar-mcp-public-docs.md and university-of-provar-mcp-course.md are maintained separately — flag for manual update. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 970cdde commit e2f6c66

8 files changed

Lines changed: 48 additions & 7 deletions

File tree

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
[![Version](https://img.shields.io/npm/v/@provartesting/provardx-cli.svg)](https://npmjs.org/package/@provartesting/provardx-cli)
44
[![Downloads/week](https://img.shields.io/npm/dw/@provartesting/provardx-cli.svg)](https://npmjs.org/package/@provartesting/provardx-cli)
55
[![License](https://img.shields.io/npm/l/@provartesting/provardx-cli.svg)](https://github.com/ProvarTesting/provardx-cli/blob/main/LICENSE.md)
6+
[![Get Access](https://img.shields.io/badge/Quality%20Hub-Get%20Access-blue)](https://aqqlrlhga7.execute-api.us-east-1.amazonaws.com/dev/auth/request-access)
67

78
# What is the ProvarDX CLI?
89

@@ -32,7 +33,7 @@ $ sf plugins uninstall @provartesting/provardx-cli
3233

3334
The Provar DX CLI includes a built-in **Model Context Protocol (MCP) server** that connects AI assistants (Claude Desktop, Claude Code, Cursor) directly to your Provar project. Once connected, an AI agent can inspect your project structure, generate Page Objects and test cases, validate every level of the test hierarchy with quality scores, and work with NitroX (Hybrid Model) component page objects for LWC, Screen Flow, Industry Components, Experience Cloud, and HTML5.
3435

35-
Validation runs in two modes: **local only** (structural rules, no key required) or **Quality Hub API** (170+ rules, quality scoring — requires a `pv_k_` API key). Run `sf provar auth login` to authenticate and unlock full validation.
36+
Validation runs in two modes: **local only** (structural rules, no key required) or **Quality Hub API** (170+ rules, quality scoring — requires a `pv_k_` API key). Run `sf provar auth login` to authenticate and unlock full validation. Don't have an account? **[Request access](https://aqqlrlhga7.execute-api.us-east-1.amazonaws.com/dev/auth/request-access)**.
3637

3738
```sh
3839
sf provar mcp start --allowed-paths /path/to/your/provar/project
@@ -111,6 +112,9 @@ DESCRIPTION
111112
~/.provar/credentials.json, and store it as the PROVAR_API_KEY environment variable
112113
or secret in your pipeline. Rotate the secret every ~90 days when the key expires.
113114
115+
Don't have an account? Request access at:
116+
https://aqqlrlhga7.execute-api.us-east-1.amazonaws.com/dev/auth/request-access
117+
114118
EXAMPLES
115119
Log in interactively:
116120

docs/mcp.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,9 @@ When `validation_source` is `local_fallback`, a `validation_warning` field is al
161161

162162
### Configuring an API key
163163

164+
**Don't have an account?** Request access at the self-service form:
165+
<https://aqqlrlhga7.execute-api.us-east-1.amazonaws.com/dev/auth/request-access>
166+
164167
**Interactive login (recommended):**
165168

166169
```sh

messages/sf.provar.auth.login.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ exchange and are then discarded — only the pv*k* API key is written to disk.
1313

1414
Run 'sf provar auth status' after login to confirm the key is configured correctly.
1515

16+
Don't have an account? Request access at:
17+
https://aqqlrlhga7.execute-api.us-east-1.amazonaws.com/dev/auth/request-access
18+
1619
# flags.url.summary
1720

1821
Override the Quality Hub API base URL (for testing against a non-production environment).

messages/sf.provar.auth.status.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ Reports where the active API key comes from (environment variable or stored file
66
shows the key prefix and when it was set, and states whether validation will use the
77
Quality Hub API or local rules only. The full key is never printed.
88

9+
If no key is configured, guidance is shown for logging in or requesting access.
10+
911
# examples
1012
- Check auth status:
1113
<%= config.bin %> <%= command.id %>

src/commands/provar/auth/login.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,12 @@ import { Flags, SfCommand } from '@salesforce/sf-plugins-core';
1010
import { Messages } from '@provartesting/provardx-plugins-utils';
1111
import { writeCredentials } from '../../../services/auth/credentials.js';
1212
import { loginFlowClient } from '../../../services/auth/loginFlow.js';
13-
import { qualityHubClient, getQualityHubBaseUrl } from '../../../services/qualityHub/client.js';
13+
import {
14+
qualityHubClient,
15+
getQualityHubBaseUrl,
16+
QualityHubAuthError,
17+
REQUEST_ACCESS_URL,
18+
} from '../../../services/qualityHub/client.js';
1419

1520
Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
1621
const messages = Messages.loadMessages('@provartesting/provardx-cli', 'sf.provar.auth.login');
@@ -78,7 +83,18 @@ export default class SfProvarAuthLogin extends SfCommand<void> {
7883

7984
// ── Step 6: Exchange Cognito access token for pv_k_ key ─────────────────
8085
// Cognito tokens are held in memory only — discarded after this call.
81-
const keyData = await qualityHubClient.exchangeTokenForKey(tokens.access_token, baseUrl);
86+
let keyData;
87+
try {
88+
keyData = await qualityHubClient.exchangeTokenForKey(tokens.access_token, baseUrl);
89+
} catch (err) {
90+
if (err instanceof QualityHubAuthError) {
91+
this.error(
92+
`No Provar MCP account found for this login.\nRequest access at: ${REQUEST_ACCESS_URL}`,
93+
{ exit: 1 }
94+
);
95+
}
96+
throw err;
97+
}
8298

8399
// ── Step 7: Persist the pv_k_ key ──────────────────────────────────────
84100
writeCredentials(keyData.api_key, keyData.prefix, 'cognito', {

src/commands/provar/auth/status.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import { SfCommand } from '@salesforce/sf-plugins-core';
1010
import { Messages } from '@provartesting/provardx-plugins-utils';
1111
import { readStoredCredentials } from '../../../services/auth/credentials.js';
12-
import { qualityHubClient, getQualityHubBaseUrl } from '../../../services/qualityHub/client.js';
12+
import { qualityHubClient, getQualityHubBaseUrl, REQUEST_ACCESS_URL } from '../../../services/qualityHub/client.js';
1313

1414
Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
1515
const messages = Messages.loadMessages('@provartesting/provardx-cli', 'sf.provar.auth.status');
@@ -82,6 +82,8 @@ export default class SfProvarAuthStatus extends SfCommand<void> {
8282
this.log('');
8383
this.log('For CI/CD: set the PROVAR_API_KEY environment variable.');
8484
this.log('');
85+
this.log(`No account? Request access at: ${REQUEST_ACCESS_URL}`);
86+
this.log('');
8587
this.log('Validation mode: local only (structural rules, no quality scoring)');
8688
}
8789
}

src/mcp/tools/testCaseValidate.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,19 @@ import {
2121
getQualityHubBaseUrl,
2222
QualityHubAuthError,
2323
QualityHubRateLimitError,
24+
REQUEST_ACCESS_URL,
2425
} from '../../services/qualityHub/client.js';
2526
import { runBestPractices } from './bestPracticesEngine.js';
2627

2728
const ONBOARDING_MESSAGE =
2829
'Quality Hub validation unavailable — running local validation only (structural rules, no quality scoring).\n' +
2930
'To enable Quality Hub (170 rules): run sf provar auth login\n' +
30-
'For CI/CD: set the PROVAR_API_KEY environment variable.';
31+
'For CI/CD: set the PROVAR_API_KEY environment variable.\n' +
32+
`No account? Request access at: ${REQUEST_ACCESS_URL}`;
3133

3234
const AUTH_WARNING =
3335
'Quality Hub API key is invalid or expired. Running local validation only.\n' +
34-
'Run sf provar auth login to get a new key.';
36+
`Run sf provar auth login to get a new key, or request access at: ${REQUEST_ACCESS_URL}`;
3537

3638
const RATE_LIMIT_WARNING = 'Quality Hub API rate limit reached. Running local validation only. Try again shortly.';
3739

src/services/qualityHub/client.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,13 @@ export async function validateTestCaseViaApi(
155155
*/
156156
const DEFAULT_QUALITY_HUB_URL = 'https://aqqlrlhga7.execute-api.us-east-1.amazonaws.com/dev';
157157

158+
/**
159+
* Self-service access request page for users who do not yet have a Provar MCP account.
160+
* Public HTML — no API key or Cognito token required.
161+
* Update when staging/prod stages are deployed.
162+
*/
163+
export const REQUEST_ACCESS_URL = `${DEFAULT_QUALITY_HUB_URL}/auth/request-access`;
164+
158165
export function getQualityHubBaseUrl(): string {
159166
return process.env.PROVAR_QUALITY_HUB_URL ?? DEFAULT_QUALITY_HUB_URL;
160167
}
@@ -191,7 +198,9 @@ export async function exchangeTokenForKey(cognitoAccessToken: string, baseUrl: s
191198
body
192199
);
193200
if (status === 401)
194-
throw new QualityHubAuthError('Account not found or no active subscription. Check your Provar licence.');
201+
throw new QualityHubAuthError(
202+
`Account not found or no active subscription.\nRequest access at: ${REQUEST_ACCESS_URL}`
203+
);
195204
if (!isOk(status)) throw new Error(`Auth exchange failed (${status}): ${responseBody}`);
196205
return JSON.parse(responseBody) as AuthExchangeResponse;
197206
}

0 commit comments

Comments
 (0)