Skip to content

fix: make configuration validation not throw #2034

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Apr 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ The SDK performs validation of required configuration options when initializing
- `clientSecret` (or `AUTH0_CLIENT_SECRET` environment variable), OR
- `clientAssertionSigningKey` (or `AUTH0_CLIENT_ASSERTION_SIGNING_KEY` environment variable)

If any of these required options are missing, the SDK will throw a `ConfigurationError` with the code `MISSING_REQUIRED_OPTIONS` and a detailed error message explaining which options are missing and how to provide them.
If any of these required options are missing, the SDK will issue a warning with a detailed message explaining which options are missing and how to provide them.

## Routes

Expand Down
55 changes: 0 additions & 55 deletions src/errors/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,58 +144,3 @@ export class AccessTokenForConnectionError extends SdkError {
this.cause = cause;
}
}

/**
* Enum representing error codes related to configuration.
*/
export enum ConfigurationErrorCode {
/**
* Missing required configuration options.
*/
MISSING_REQUIRED_OPTIONS = "missing_required_options"
}

/**
* Error class representing a configuration error.
* Extends the `SdkError` class.
*/
export class ConfigurationError extends SdkError {
/**
* The error code associated with the configuration error.
*/
public code: string;
public missingOptions?: string[];

/**
* Constructs a new `ConfigurationError` instance.
*
* @param code - The error code.
* @param missingOptions - Array of missing configuration option names.
* @param envVarMapping - Optional mapping of option names to their environment variable names.
*/
constructor(
code: string,
missingOptions: string[] = [],
envVarMapping: Record<string, string> = {}
) {
// Standard intro message explaining the issue
let errorMessage =
"Not all required options where provided when creating an instance of Auth0Client. Ensure to provide all missing options, either by passing it to the Auth0Client constructor, or by setting the corresponding environment variable.\n\n";

// Add specific details for each missing option
missingOptions.forEach((key) => {
if (key === "clientAuthentication") {
errorMessage += `Missing: clientAuthentication: Set either AUTH0_CLIENT_SECRET env var or AUTH0_CLIENT_ASSERTION_SIGNING_KEY env var, or pass clientSecret or clientAssertionSigningKey in options\n`;
} else if (envVarMapping[key]) {
errorMessage += `Missing: ${key}: Set ${envVarMapping[key]} env var or pass ${key} in options\n`;
} else {
errorMessage += `Missing: ${key}\n`;
}
});

super(errorMessage.trim());
this.name = "ConfigurationError";
this.code = code;
this.missingOptions = missingOptions;
}
}
68 changes: 0 additions & 68 deletions src/server/client.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";

import { ConfigurationError, ConfigurationErrorCode } from "../errors/index.js";
import { Auth0Client } from "./client.js";

describe("Auth0Client", () => {
Expand Down Expand Up @@ -37,73 +36,6 @@ describe("Auth0Client", () => {
});

describe("constructor validation", () => {
it("should throw ConfigurationError when all required options are missing", () => {
expect(() => new Auth0Client()).toThrow(ConfigurationError);

try {
new Auth0Client();
} catch (error) {
const configError = error as ConfigurationError;
expect(configError).toBeInstanceOf(ConfigurationError);
expect(configError.code).toBe(
ConfigurationErrorCode.MISSING_REQUIRED_OPTIONS
);
expect(configError.missingOptions).toContain("domain");
expect(configError.missingOptions).toContain("clientId");
expect(configError.missingOptions).toContain("clientAuthentication");
expect(configError.missingOptions).toContain("appBaseUrl");
expect(configError.missingOptions).toContain("secret");

// Check that error message contains specific text
expect(configError.message).toContain(
"Not all required options where provided"
);
expect(configError.message).toContain(ENV_VARS.DOMAIN);
expect(configError.message).toContain(ENV_VARS.CLIENT_ID);
expect(configError.message).toContain(ENV_VARS.CLIENT_SECRET);
expect(configError.message).toContain(
ENV_VARS.CLIENT_ASSERTION_SIGNING_KEY
);
expect(configError.message).toContain(ENV_VARS.APP_BASE_URL);
expect(configError.message).toContain(ENV_VARS.SECRET);
}
});

it("should throw ConfigurationError when some required options are missing", () => {
// Provide some but not all required options
const options = {
domain: "example.auth0.com",
clientId: "client_123"
};

try {
new Auth0Client(options);
} catch (error) {
const configError = error as ConfigurationError;
expect(configError).toBeInstanceOf(ConfigurationError);
expect(configError.code).toBe(
ConfigurationErrorCode.MISSING_REQUIRED_OPTIONS
);
// These should be missing
expect(configError.missingOptions).toContain("clientAuthentication");
expect(configError.missingOptions).toContain("appBaseUrl");
expect(configError.missingOptions).toContain("secret");
// These should not be in the missing list
expect(configError.missingOptions).not.toContain("domain");
expect(configError.missingOptions).not.toContain("clientId");

// Error message should only contain instructions for missing options
expect(configError.message).toContain(ENV_VARS.CLIENT_SECRET);
expect(configError.message).toContain(
ENV_VARS.CLIENT_ASSERTION_SIGNING_KEY
);
expect(configError.message).toContain(ENV_VARS.APP_BASE_URL);
expect(configError.message).toContain(ENV_VARS.SECRET);
expect(configError.message).not.toContain(`Set ${ENV_VARS.DOMAIN}`);
expect(configError.message).not.toContain(`Set ${ENV_VARS.CLIENT_ID}`);
}
});

it("should accept clientSecret as authentication method", () => {
// Set required environment variables with clientSecret
process.env[ENV_VARS.DOMAIN] = "env.auth0.com";
Expand Down
25 changes: 17 additions & 8 deletions src/server/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@ import {
AccessTokenError,
AccessTokenErrorCode,
AccessTokenForConnectionError,
AccessTokenForConnectionErrorCode,
ConfigurationError,
ConfigurationErrorCode
AccessTokenForConnectionErrorCode
} from "../errors";
import {
AccessTokenForConnectionOptions,
Expand Down Expand Up @@ -694,11 +692,22 @@ export class Auth0Client {
secret: "AUTH0_SECRET"
};

throw new ConfigurationError(
ConfigurationErrorCode.MISSING_REQUIRED_OPTIONS,
missing,
envVarNames
);
// Standard intro message explaining the issue
let errorMessage =
"WARNING: Not all required options where provided when creating an instance of Auth0Client. Ensure to provide all missing options, either by passing it to the Auth0Client constructor, or by setting the corresponding environment variable.\n";

// Add specific details for each missing option
missing.forEach((key) => {
if (key === "clientAuthentication") {
errorMessage += `Missing: clientAuthentication: Set either AUTH0_CLIENT_SECRET env var or AUTH0_CLIENT_ASSERTION_SIGNING_KEY env var, or pass clientSecret or clientAssertionSigningKey in options\n`;
} else if (envVarNames[key]) {
errorMessage += `Missing: ${key}: Set ${envVarNames[key]} env var or pass ${key} in options\n`;
} else {
errorMessage += `Missing: ${key}\n`;
}
});

console.error(errorMessage.trim());
}

// Prepare the result object with all validated options
Expand Down