feat(auth0): Add My Organization API skill#91
Conversation
📝 WalkthroughWalkthroughThis PR introduces a complete Auth0 skill plugin for building B2B delegated admin portals using the My Organization API. It includes comprehensive documentation (setup guide, API reference, backend patterns, advanced features), working code examples across five frameworks, and CLI automation tools for tenant configuration, project introspection, and setup verification. ChangesAuth0 My Organization API Skill Plugin
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Tip 💬 Introducing Slack Agent: The best way for teams to turn conversations into code.Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.
Built for teams:
One agent for your entire SDLC. Right inside Slack. Comment |
There was a problem hiding this comment.
Actionable comments posted: 11
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
plugins/auth0/skills/auth0-my-organization-api/skills/using-my-organization-api/scripts/verify-setup.mjs (1)
161-172:⚠️ Potential issue | 🟠 Major | ⚡ Quick winVerification should fail process exit code when checks fail.
Line 162 and Line 171 always report success/exit 0, even when checks fail. That breaks CI gating and downstream automation.
💡 Suggested fix
const result = { - status: "success", + status: allPassed ? "success" : "error", data: { @@ console.log(JSON.stringify(result, null, 2)); -process.exit(0); +process.exit(allPassed ? 0 : 1);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@plugins/auth0/skills/auth0-my-organization-api/skills/using-my-organization-api/scripts/verify-setup.mjs` around lines 161 - 172, The script currently always sets result.status to "success" and calls process.exit(0) even when checks fail; change the logic that builds the result and exits so that if allPassed is false you set result.status to "failure" (or "failed") and call process.exit(1) (or another non-zero code), otherwise keep status "success" and exit 0; update the block that constructs result (including the summary) and replace the unconditional process.exit(0) with a conditional exit based on allPassed.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@plugins/auth0/skills/auth0-my-organization-api/skills/using-my-organization-api/references/advanced.md`:
- Around line 99-114: The token customization currently implemented in
onExecutePostLogin runs only for interactive user logins; move this logic into a
credentials-exchange rule/trigger so it executes for Client Credentials (M2M)
flows: create a credentials-exchange handler that extracts
accessToken.getCustomClaim('org_id'), calls getOrganizationPermissions(orgId)
and sets the same claims via api.accessToken.setCustomClaim('organization_id',
orgId) and api.accessToken.setCustomClaim('org_permissions', ...); remove or
disable the onExecutePostLogin usage for M2M and ensure the new
credentials-exchange trigger references the same getOrganizationPermissions
helper and uses the provided api object for setting access token claims.
In
`@plugins/auth0/skills/auth0-my-organization-api/skills/using-my-organization-api/references/backend.md`:
- Around line 22-27: The middleware uses NextResponse.redirect with plain string
paths; change both redirects to construct absolute URLs using new URL(...,
request.url) so they resolve correctly in middleware: replace
NextResponse.redirect('/api/auth/login') and
NextResponse.redirect('/onboarding') with NextResponse.redirect(new
URL('/api/auth/login', request.url)) and NextResponse.redirect(new
URL('/onboarding', request.url)) respectively, keeping the existing session
check (if (!session.user.org_id)) and request variable names intact.
In
`@plugins/auth0/skills/auth0-my-organization-api/skills/using-my-organization-api/scripts/detect-stack.mjs`:
- Around line 76-77: The script currently reads AUTH0_CLIENT_SECRET into
config.clientSecret and later serializes the whole config to stdout (via
JSON.stringify or similar), which leaks secrets; instead, do not populate
config.clientSecret from process.env.AUTH0_CLIENT_SECRET (or if it must be read,
ensure you remove or replace config.clientSecret with a redacted placeholder
like "<redacted>" before any output) and update the serialization step that
writes to stdout so it only emits non-secret fields (e.g., domain and clientId)
or a redacted config; refer to the config object and any code that assigns from
process.env.AUTH0_CLIENT_SECRET and the
JSON.stringify/console.log/process.stdout.write call that prints the config and
ensure the secret is never included in that output.
In
`@plugins/auth0/skills/auth0-my-organization-api/skills/using-my-organization-api/scripts/extract-theme.mjs`:
- Around line 228-241: The generated CSS always emits the hardcoded ".dark { ...
}" block; change it to use the detected dark selector variable (darkSelector)
instead so dark-mode overrides are emitted as `${darkSelector} { ... }`; update
all occurrences where ".dark {" is emitted (the block that checks hasDarkMode &&
Object.keys(darkColors).length > 0 and any other places emitting dark-mode
overrides) and keep the same inner logic that switches on cssPath and prefixes
(--name vs --auth0-name).
In
`@plugins/auth0/skills/auth0-my-organization-api/skills/using-my-organization-api/scripts/utils/clients.mjs`:
- Around line 103-105: The condition and read use the wrong field name: replace
references to client.allowed_web_origins with client.web_origins so the SPA
check uses the actual Auth0 response property; when building updates.web_origins
(the symbol already used), merge existing client.web_origins (or empty array)
with baseUrl instead of reading the undefined allowed_web_origins to avoid
clobbering existing origins in the update.
In
`@plugins/auth0/skills/auth0-my-organization-api/skills/using-my-organization-api/scripts/utils/discovery.mjs`:
- Around line 60-63: The discovery code currently calls auth0ApiCall for key
resources (e.g., the call assigned to profiles and stored into
resources.connectionProfiles) and silently proceeds when the call fails; update
each discovery block that calls auth0ApiCall (for "connection-profiles" and the
two other similar calls) to await the promise, check the response.ok flag and
presence of response.data, and if the call failed log the error details (include
the response or thrown error) and either throw or return an early failure
instead of treating the resource as simply empty; specifically modify the blocks
that reference auth0ApiCall and assign to resources.connectionProfiles,
resources.identityProviders, and resources.roles so failures are surfaced with
context rather than swallowed.
In
`@plugins/auth0/skills/auth0-my-organization-api/skills/using-my-organization-api/scripts/utils/resources.mjs`:
- Around line 191-196: The org-connection enablement calls currently ignore the
auth0ApiCall response so failures are swallowed; update the code around the
auth0ApiCall invocations that post to
`organizations/${org.id}/enabled_connections` (both the block calling
auth0ApiCall at the earlier location and the later block around lines 204-209)
to inspect the returned result and propagate errors: await the auth0ApiCall,
check that the response indicates success (e.g., result === "created" || result
=== "updated" or HTTP success), and if not throw or return a descriptive error
so callers can detect failure instead of assuming success; ensure you preserve
existing behavior on success and surface the underlying API error message when
available.
- Line 157: The code calls JSON.parse on CLI output directly (const role =
JSON.parse(createResult.stdout); and the similar parse at line 189) which can
throw on non-JSON output; wrap the JSON.parse calls for createResult.stdout (and
the other stdout parse) in a try/catch (or use a safe-parse helper) to handle
malformed output gracefully, log the parse error and the raw stdout (using the
same process/logger used elsewhere) and then either fail the operation with a
clear error or return a safe fallback so the bootstrap does not hard-crash;
update references to role and the second parsed variable accordingly so callers
handle the possible undefined/error path.
- Around line 98-102: The update check in the needsUpdate logic omits comparing
enabled_features so resources with drift there are skipped; update the boolean
expression in needsUpdate (the variable used around existing.organization,
desiredConfig.organization and connection_name_prefix_template) to also compare
existing.enabled_features against desiredConfig.enabled_features (or a
normalized deep-equality check) so differences in enabled_features trigger an
update action instead of returning skip.
In
`@plugins/auth0/skills/auth0-my-organization-api/skills/using-my-organization-api/scripts/verify-setup.mjs`:
- Around line 37-47: The script calls .includes() on the value returned by
readFile without guarding against a failed or undefined read; update the logic
around readFile and content so you first validate that content is a non-empty
string (or coerce to "") before running the required/filter checks (the
variables/functions to update are readFile, content, required, and the missing
filter), and if the file could not be read return a clear error or exit early
rather than performing .includes() on null/undefined.
In
`@plugins/auth0/skills/auth0-my-organization-api/skills/using-my-organization-api/SKILL.md`:
- Line 78: Several fenced code blocks in SKILL.md are missing language
identifiers (violating MD040); update each triple-backtick block (the anonymous
``` fences) to include an appropriate language tag (e.g., ```bash, ```json,
```text) so markdownlint passes and readability improves—search for the unnamed
``` blocks in the SKILL.md content and replace them with language-tagged fences
that match the block contents.
---
Outside diff comments:
In
`@plugins/auth0/skills/auth0-my-organization-api/skills/using-my-organization-api/scripts/verify-setup.mjs`:
- Around line 161-172: The script currently always sets result.status to
"success" and calls process.exit(0) even when checks fail; change the logic that
builds the result and exits so that if allPassed is false you set result.status
to "failure" (or "failed") and call process.exit(1) (or another non-zero code),
otherwise keep status "success" and exit 0; update the block that constructs
result (including the summary) and replace the unconditional process.exit(0)
with a conditional exit based on allPassed.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 423e9152-973a-4c6a-9255-a32351fd0f63
📒 Files selected for processing (16)
plugins/auth0/skills/auth0-my-organization-api/.claude-plugin/plugin.jsonplugins/auth0/skills/auth0-my-organization-api/README.mdplugins/auth0/skills/auth0-my-organization-api/skills/using-my-organization-api/SKILL.mdplugins/auth0/skills/auth0-my-organization-api/skills/using-my-organization-api/references/advanced.mdplugins/auth0/skills/auth0-my-organization-api/skills/using-my-organization-api/references/api.mdplugins/auth0/skills/auth0-my-organization-api/skills/using-my-organization-api/references/backend.mdplugins/auth0/skills/auth0-my-organization-api/skills/using-my-organization-api/references/examples.mdplugins/auth0/skills/auth0-my-organization-api/skills/using-my-organization-api/scripts/bootstrap.mjsplugins/auth0/skills/auth0-my-organization-api/skills/using-my-organization-api/scripts/detect-stack.mjsplugins/auth0/skills/auth0-my-organization-api/skills/using-my-organization-api/scripts/extract-theme.mjsplugins/auth0/skills/auth0-my-organization-api/skills/using-my-organization-api/scripts/utils/auth0-api.mjsplugins/auth0/skills/auth0-my-organization-api/skills/using-my-organization-api/scripts/utils/clients.mjsplugins/auth0/skills/auth0-my-organization-api/skills/using-my-organization-api/scripts/utils/discovery.mjsplugins/auth0/skills/auth0-my-organization-api/skills/using-my-organization-api/scripts/utils/resources.mjsplugins/auth0/skills/auth0-my-organization-api/skills/using-my-organization-api/scripts/validate-auth0.mjsplugins/auth0/skills/auth0-my-organization-api/skills/using-my-organization-api/scripts/verify-setup.mjs
| exports.onExecutePostLogin = async (event, api) => { | ||
| const { client, accessToken } = event | ||
|
|
||
| // Only apply to M2M clients with organization scope | ||
| if (client.client_id !== process.env.MY_ORG_API_CLIENT_ID) { | ||
| return | ||
| } | ||
|
|
||
| // Add organization context to M2M tokens | ||
| // This allows API calls to be scoped to specific organizations | ||
| const orgId = accessToken.getCustomClaim('org_id') | ||
| if (orgId) { | ||
| api.accessToken.setCustomClaim('organization_id', orgId) | ||
| api.accessToken.setCustomClaim('org_permissions', await getOrganizationPermissions(orgId)) | ||
| } | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Which Auth0 Action trigger is used to customize access tokens issued via client credentials (M2M): post-login or credentials-exchange?
💡 Result:
The Auth0 Action trigger used to customize access tokens issued via client credentials (M2M) is credentials-exchange [1][2][3]. This trigger runs as part of the Machine to Machine Flow when an access token is issued via the Client Credentials Flow, allowing customization such as adding custom claims or denying the token [1][4][2]. The post-login trigger is for user login flows, not M2M [3].
Citations:
- 1: https://auth0.com/docs/customize/actions/flows-and-triggers/machine-to-machine-flow
- 2: https://auth0.com/docs/customize/actions/explore-triggers/machine-to-machine-trigger
- 3: https://auth0.com/docs/customize/actions/actions-overview
- 4: https://auth0.com/docs/customize/actions/explore-triggers/machine-to-machine-trigger/credentials-exchange-event-object
Use credentials-exchange trigger for M2M token customization, not onExecutePostLogin.
The onExecutePostLogin trigger only fires for user authentication flows. For Machine-to-Machine (M2M) flows using the Client Credentials grant, the credentials-exchange trigger must be used instead. Move this token customization logic to a credentials-exchange trigger so it executes when M2M access tokens are issued.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@plugins/auth0/skills/auth0-my-organization-api/skills/using-my-organization-api/references/advanced.md`
around lines 99 - 114, The token customization currently implemented in
onExecutePostLogin runs only for interactive user logins; move this logic into a
credentials-exchange rule/trigger so it executes for Client Credentials (M2M)
flows: create a credentials-exchange handler that extracts
accessToken.getCustomClaim('org_id'), calls getOrganizationPermissions(orgId)
and sets the same claims via api.accessToken.setCustomClaim('organization_id',
orgId) and api.accessToken.setCustomClaim('org_permissions', ...); remove or
disable the onExecutePostLogin usage for M2M and ensure the new
credentials-exchange trigger references the same getOrganizationPermissions
helper and uses the provided api object for setting access token claims.
| return NextResponse.redirect('/api/auth/login') | ||
| } | ||
|
|
||
| // Check if user has organization context | ||
| if (!session.user.org_id) { | ||
| return NextResponse.redirect('/onboarding') |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
For current Next.js middleware, what is the recommended NextResponse.redirectusage: string path ornew URL(path, request.url)?
💡 Result:
For current Next.js middleware (as of 2026, Next.js 15+), the recommended usage of NextResponse.redirect is new URL(path, request.url) rather than a plain string path.[1][2][3] Official Next.js documentation consistently shows examples using new URL('/path', request.url) in middleware and NextResponse.redirect sections.[4][1][2][3] This approach ensures the redirect URL is properly resolved relative to the current request's base URL (origin and protocol), avoiding issues with relative paths in middleware contexts where the base might not be assumed as the app's root.[1] While NextResponse.redirect technically accepts a string (as it extends Response.redirect, which takes string|URL), using a plain relative string like NextResponse.redirect('/home') risks incorrect resolution if the request base differs (e.g., proxies, custom domains). The docs emphasize constructing a full URL object for reliability, especially when modifying search params: const url = new URL('/login', request.url); url.searchParams.set('from', request.nextUrl.pathname); return NextResponse.redirect(url);[1][2] No official sources recommend plain strings for middleware redirects; all examples use new URL.[4][3]
Citations:
- 1: https://nextjs.org/docs/app/api-reference/functions/next-response
- 2: https://nextjs.im/docs/15/app/api-reference/functions/next-response/
- 3: https://nextjs.im/docs/15/app/api-reference/file-conventions/middleware/
- 4: https://nextjs.org/docs/14/app/building-your-application/routing/middleware
Use new URL() construction for middleware redirects.
Lines 22 and 27 use plain string paths with NextResponse.redirect(). In Next.js middleware, these must use new URL(path, request.url) to properly resolve the full URL relative to the request base.
Suggested fix
- return NextResponse.redirect('/api/auth/login')
+ return NextResponse.redirect(new URL('/api/auth/login', request.url))
...
- return NextResponse.redirect('/onboarding')
+ return NextResponse.redirect(new URL('/onboarding', request.url))📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| return NextResponse.redirect('/api/auth/login') | |
| } | |
| // Check if user has organization context | |
| if (!session.user.org_id) { | |
| return NextResponse.redirect('/onboarding') | |
| return NextResponse.redirect(new URL('/api/auth/login', request.url)) | |
| } | |
| // Check if user has organization context | |
| if (!session.user.org_id) { | |
| return NextResponse.redirect(new URL('/onboarding', request.url)) |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@plugins/auth0/skills/auth0-my-organization-api/skills/using-my-organization-api/references/backend.md`
around lines 22 - 27, The middleware uses NextResponse.redirect with plain
string paths; change both redirects to construct absolute URLs using new
URL(..., request.url) so they resolve correctly in middleware: replace
NextResponse.redirect('/api/auth/login') and
NextResponse.redirect('/onboarding') with NextResponse.redirect(new
URL('/api/auth/login', request.url)) and NextResponse.redirect(new
URL('/onboarding', request.url)) respectively, keeping the existing session
check (if (!session.user.org_id)) and request variable names intact.
| const config = { domain: null, clientId: null, clientSecret: null }; | ||
| if (envFile) { |
There was a problem hiding this comment.
Do not emit AUTH0_CLIENT_SECRET in detection output.
Line 93-Line 95 read the raw secret and Line 183 serializes it to stdout. This can leak credentials in terminal history and CI logs.
💡 Suggested fix
- const config = { domain: null, clientId: null, clientSecret: null };
+ const config = { domain: null, clientId: null, hasClientSecret: false };
@@
- } else if (key === "AUTH0_CLIENT_SECRET") {
- config.clientSecret = val;
+ } else if (key === "AUTH0_CLIENT_SECRET") {
+ config.hasClientSecret = true;
}Also applies to: 93-95, 101-101, 183-183
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@plugins/auth0/skills/auth0-my-organization-api/skills/using-my-organization-api/scripts/detect-stack.mjs`
around lines 76 - 77, The script currently reads AUTH0_CLIENT_SECRET into
config.clientSecret and later serializes the whole config to stdout (via
JSON.stringify or similar), which leaks secrets; instead, do not populate
config.clientSecret from process.env.AUTH0_CLIENT_SECRET (or if it must be read,
ensure you remove or replace config.clientSecret with a redacted placeholder
like "<redacted>" before any output) and update the serialization step that
writes to stdout so it only emits non-secret fields (e.g., domain and clientId)
or a redacted config; refer to the config object and any code that assigns from
process.env.AUTH0_CLIENT_SECRET and the
JSON.stringify/console.log/process.stdout.write call that prints the config and
ensure the secret is never included in that output.
| if (hasDarkMode && Object.keys(darkColors).length > 0) { | ||
| lines.push(""); | ||
| lines.push(".dark {"); | ||
| if (cssPath === "tailwind") { | ||
| for (const [name, value] of Object.entries(darkColors)) { | ||
| lines.push(` --${name}: ${value};`); | ||
| } | ||
| } else { | ||
| for (const [name, value] of Object.entries(darkColors)) { | ||
| lines.push(` --auth0-${name}: ${value};`); | ||
| } | ||
| } | ||
| lines.push("}"); | ||
| } |
There was a problem hiding this comment.
Use the detected dark selector when emitting dark-mode overrides.
Line 303-Line 305 correctly detect whether dark mode is .dark or [data-theme="dark"], but Line 230 always emits .dark { ... }. That makes generated CSS incorrect for projects using data-attribute dark mode.
💡 Suggested fix
-function generateOverrideBlock(colors, cssPath, darkColors, hasDarkMode) {
+function generateOverrideBlock(colors, cssPath, darkColors, hasDarkMode, darkSelector = ".dark") {
const lines = ["/* Auth0 Universal Components — apply this entire block verbatim */", ":root {"];
@@
if (hasDarkMode && Object.keys(darkColors).length > 0) {
lines.push("");
- lines.push(".dark {");
+ lines.push(`${darkSelector} {`);
@@
-const generatedOverrideBlock = generateOverrideBlock(colors, opts.cssPath, darkColors, hasDarkMode);
+const generatedOverrideBlock = generateOverrideBlock(colors, opts.cssPath, darkColors, hasDarkMode, darkSelector || ".dark");Also applies to: 303-305, 318-318
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@plugins/auth0/skills/auth0-my-organization-api/skills/using-my-organization-api/scripts/extract-theme.mjs`
around lines 228 - 241, The generated CSS always emits the hardcoded ".dark {
... }" block; change it to use the detected dark selector variable
(darkSelector) instead so dark-mode overrides are emitted as `${darkSelector} {
... }`; update all occurrences where ".dark {" is emitted (the block that checks
hasDarkMode && Object.keys(darkColors).length > 0 and any other places emitting
dark-mode overrides) and keep the same inner logic that switches on cssPath and
prefixes (--name vs --auth0-name).
| if (appType === "spa" && !client.allowed_web_origins?.includes(baseUrl)) { | ||
| updates.web_origins = [...(client.allowed_web_origins || []), baseUrl]; | ||
| needsUpdate = true; |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Auth0 Management API for clients: what is the correct property name for SPA web origins in GET/PATCH payloads (web_originsvsallowed_web_origins)?
💡 Result:
The correct property name for SPA web origins in Auth0 Management API GET and PATCH payloads for clients is "web_origins". This is confirmed across multiple sources, including official Auth0.NET SDK models which map [JsonProperty("web_origins")] to WebOrigins, describing it as "A set of allowed origins for use with Cross-Origin Authentication and web message response mode" [1][2]. SDK examples for creating and updating SPA clients explicitly use WebOrigins: []string{...} [3]. Community discussions refer to setting web_origins via the API [4]. Note that "allowed_origins" (or allowedOrigins) is a separate property for CORS origins [1][5][6][3]. No sources mention "allowed_web_origins"; "web_origins" is the consistent JSON property name for responses (GET) and requests (POST/PATCH) [1][5].
Citations:
- 1: https://auth0.github.io/auth0.net/api/Auth0.ManagementApi.Models.ClientBase.html
- 2: https://auth0.github.io/auth0.net/api/Auth0.ManagementApi.Models.html
- 3: https://mintlify.com/auth0/go-auth0/guides/working-with-clients
- 4: https://community.auth0.com/t/setting-web-origins-when-creating-a-client-using-the-management-api/9130
- 5: https://www.withone.ai/knowledge/auth0-management/conn_mod_def%3A%3AGJ0DKufDoIE%3A%3An8ZaKp-mTvy2zRv4bZUS2w
- 6: https://auth0.com/docs/api/management/v2/clients/patch-clients-by-id
🏁 Script executed:
cat -n plugins/auth0/skills/auth0-my-organization-api/skills/using-my-organization-api/scripts/utils/clients.mjs | sed -n '90,115p'Repository: auth0/agent-skills
Length of output: 1254
🏁 Script executed:
rg "allowed_web_origins|web_origins" plugins/auth0/skills/auth0-my-organization-api/skills/using-my-organization-api/scripts/utils/clients.mjs -n -A 2 -B 2Repository: auth0/agent-skills
Length of output: 492
Use the correct origins field to avoid clobbering SPA config.
At lines 103-104, the code checks/reads allowed_web_origins, but Auth0 API responses use web_origins and the payload is already correctly set to web_origins at line 38. This mismatch causes the condition to evaluate against an undefined property and loses existing origins when updating.
Suggested fix
- if (appType === "spa" && !client.allowed_web_origins?.includes(baseUrl)) {
- updates.web_origins = [...(client.allowed_web_origins || []), baseUrl];
+ if (appType === "spa" && !client.web_origins?.includes(baseUrl)) {
+ updates.web_origins = [...(client.web_origins || []), baseUrl];
needsUpdate = true;
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (appType === "spa" && !client.allowed_web_origins?.includes(baseUrl)) { | |
| updates.web_origins = [...(client.allowed_web_origins || []), baseUrl]; | |
| needsUpdate = true; | |
| if (appType === "spa" && !client.web_origins?.includes(baseUrl)) { | |
| updates.web_origins = [...(client.web_origins || []), baseUrl]; | |
| needsUpdate = true; |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@plugins/auth0/skills/auth0-my-organization-api/skills/using-my-organization-api/scripts/utils/clients.mjs`
around lines 103 - 105, The condition and read use the wrong field name: replace
references to client.allowed_web_origins with client.web_origins so the SPA
check uses the actual Auth0 response property; when building updates.web_origins
(the symbol already used), merge existing client.web_origins (or empty array)
with baseUrl instead of reading the undefined allowed_web_origins to avoid
clobbering existing origins in the update.
| const needsUpdate = | ||
| existing.organization?.show_as_button !== desiredConfig.organization.show_as_button || | ||
| existing.organization?.assign_membership_on_login !== desiredConfig.organization.assign_membership_on_login || | ||
| existing.connection_name_prefix_template !== desiredConfig.connection_name_prefix_template; | ||
| if (!needsUpdate) return { action: "skip", data: existing }; |
There was a problem hiding this comment.
enabled_features drift is not part of update check.
Line 98-Line 102 skip update decisions without validating enabled_features, so stale profiles can be incorrectly treated as up-to-date.
💡 Suggested fix
const needsUpdate =
existing.organization?.show_as_button !== desiredConfig.organization.show_as_button ||
existing.organization?.assign_membership_on_login !== desiredConfig.organization.assign_membership_on_login ||
- existing.connection_name_prefix_template !== desiredConfig.connection_name_prefix_template;
+ existing.connection_name_prefix_template !== desiredConfig.connection_name_prefix_template ||
+ JSON.stringify([...(existing.enabled_features || [])].sort()) !==
+ JSON.stringify([...(desiredConfig.enabled_features || [])].sort());📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const needsUpdate = | |
| existing.organization?.show_as_button !== desiredConfig.organization.show_as_button || | |
| existing.organization?.assign_membership_on_login !== desiredConfig.organization.assign_membership_on_login || | |
| existing.connection_name_prefix_template !== desiredConfig.connection_name_prefix_template; | |
| if (!needsUpdate) return { action: "skip", data: existing }; | |
| const needsUpdate = | |
| existing.organization?.show_as_button !== desiredConfig.organization.show_as_button || | |
| existing.organization?.assign_membership_on_login !== desiredConfig.organization.assign_membership_on_login || | |
| existing.connection_name_prefix_template !== desiredConfig.connection_name_prefix_template || | |
| JSON.stringify([...(existing.enabled_features || [])].sort()) !== | |
| JSON.stringify([...(desiredConfig.enabled_features || [])].sort()); | |
| if (!needsUpdate) return { action: "skip", data: existing }; |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@plugins/auth0/skills/auth0-my-organization-api/skills/using-my-organization-api/scripts/utils/resources.mjs`
around lines 98 - 102, The update check in the needsUpdate logic omits comparing
enabled_features so resources with drift there are skipped; update the boolean
expression in needsUpdate (the variable used around existing.organization,
desiredConfig.organization and connection_name_prefix_template) to also compare
existing.enabled_features against desiredConfig.enabled_features (or a
normalized deep-equality check) so differences in enabled_features trigger an
update action instead of returning skip.
| { timeout: 15000 } | ||
| ); | ||
| if (!createResult.ok) return { action: "error", error: createResult.stderr }; | ||
| const role = JSON.parse(createResult.stdout); |
There was a problem hiding this comment.
Guard JSON parsing of CLI output to avoid hard crashes.
Line 157 and Line 189 parse CLI stdout without a try/catch. Any unexpected non-JSON output will throw and terminate bootstrap.
💡 Suggested fix
- const role = JSON.parse(createResult.stdout);
+ let role;
+ try {
+ role = JSON.parse(createResult.stdout);
+ } catch {
+ return { action: "error", error: "Unable to parse role creation response" };
+ }
@@
- const org = JSON.parse(createResult.stdout);
+ let org;
+ try {
+ org = JSON.parse(createResult.stdout);
+ } catch {
+ return { action: "error", error: "Unable to parse organization creation response" };
+ }Also applies to: 189-189
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@plugins/auth0/skills/auth0-my-organization-api/skills/using-my-organization-api/scripts/utils/resources.mjs`
at line 157, The code calls JSON.parse on CLI output directly (const role =
JSON.parse(createResult.stdout); and the similar parse at line 189) which can
throw on non-JSON output; wrap the JSON.parse calls for createResult.stdout (and
the other stdout parse) in a try/catch (or use a safe-parse helper) to handle
malformed output gracefully, log the parse error and the raw stdout (using the
same process/logger used elsewhere) and then either fail the operation with a
clear error or return a safe fallback so the bootstrap does not hard-crash;
update references to role and the second parsed variable accordingly so callers
handle the possible undefined/error path.
| auth0ApiCall("post", `organizations/${org.id}/enabled_connections`, { | ||
| connection_id: connectionId, | ||
| assign_membership_on_login: false, | ||
| is_signup_enabled: false, | ||
| }); | ||
| } |
There was a problem hiding this comment.
Propagate failures when enabling organization connections.
Line 191-Line 196 and Line 204-Line 209 ignore the API response. The function can return "created"/"updated" even if connection enablement fails.
💡 Suggested fix
if (connectionId) {
- auth0ApiCall("post", `organizations/${org.id}/enabled_connections`, {
+ const enableResult = auth0ApiCall("post", `organizations/${org.id}/enabled_connections`, {
connection_id: connectionId,
assign_membership_on_login: false,
is_signup_enabled: false,
});
+ if (!enableResult.ok) return { action: "error", error: enableResult.error };
}
@@
if (!connCheck.ok || !connCheck.data?.connection) {
- auth0ApiCall("post", `organizations/${existing.id}/enabled_connections`, {
+ const enableResult = auth0ApiCall("post", `organizations/${existing.id}/enabled_connections`, {
connection_id: connectionId,
assign_membership_on_login: false,
is_signup_enabled: false,
});
+ if (!enableResult.ok) return { action: "error", error: enableResult.error };
return { action: "updated", data: existing };
}Also applies to: 204-209
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@plugins/auth0/skills/auth0-my-organization-api/skills/using-my-organization-api/scripts/utils/resources.mjs`
around lines 191 - 196, The org-connection enablement calls currently ignore the
auth0ApiCall response so failures are swallowed; update the code around the
auth0ApiCall invocations that post to
`organizations/${org.id}/enabled_connections` (both the block calling
auth0ApiCall at the earlier location and the later block around lines 204-209)
to inspect the returned result and propagate errors: await the auth0ApiCall,
check that the response indicates success (e.g., result === "created" || result
=== "updated" or HTTP success), and if not throw or return a descriptive error
so callers can detect failure instead of assuming success; ensure you preserve
existing behavior on success and surface the underlying API error message when
available.
| const content = readFile(join(root, envPath)); | ||
| const required = framework === "nextjs" | ||
| ? ["AUTH0_DOMAIN", "AUTH0_CLIENT_ID", "AUTH0_CLIENT_SECRET", "AUTH0_SECRET"] | ||
| : ["AUTH0_DOMAIN", "AUTH0_CLIENT_ID"]; | ||
| // Also check VITE_ prefixed vars for react-spa | ||
| const missing = required.filter((v) => { | ||
| if (content.includes(v + "=")) return false; | ||
| if (framework === "react-spa" && content.includes("VITE_" + v.replace("AUTH0_", "AUTH0_") + "=")) return false; | ||
| if (framework === "react-spa" && content.includes(`VITE_${v}=`)) return false; | ||
| return true; | ||
| }); |
There was a problem hiding this comment.
Guard against unreadable env files before string checks.
Line 43-Line 46 call .includes() on content without null guard. If file read fails (permissions/encoding issues), this throws and aborts the script.
💡 Suggested fix
const content = readFile(join(root, envPath));
+ if (content == null) {
+ return {
+ name: "env_vars_present",
+ pass: false,
+ details: `Cannot read ${envPath}`,
+ fix: `Ensure ${envPath} is readable and contains required Auth0 variables`,
+ };
+ }
const required = framework === "nextjs"📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const content = readFile(join(root, envPath)); | |
| const required = framework === "nextjs" | |
| ? ["AUTH0_DOMAIN", "AUTH0_CLIENT_ID", "AUTH0_CLIENT_SECRET", "AUTH0_SECRET"] | |
| : ["AUTH0_DOMAIN", "AUTH0_CLIENT_ID"]; | |
| // Also check VITE_ prefixed vars for react-spa | |
| const missing = required.filter((v) => { | |
| if (content.includes(v + "=")) return false; | |
| if (framework === "react-spa" && content.includes("VITE_" + v.replace("AUTH0_", "AUTH0_") + "=")) return false; | |
| if (framework === "react-spa" && content.includes(`VITE_${v}=`)) return false; | |
| return true; | |
| }); | |
| const content = readFile(join(root, envPath)); | |
| if (content == null) { | |
| return { | |
| name: "env_vars_present", | |
| pass: false, | |
| details: `Cannot read ${envPath}`, | |
| fix: `Ensure ${envPath} is readable and contains required Auth0 variables`, | |
| }; | |
| } | |
| const required = framework === "nextjs" | |
| ? ["AUTH0_DOMAIN", "AUTH0_CLIENT_ID", "AUTH0_CLIENT_SECRET", "AUTH0_SECRET"] | |
| : ["AUTH0_DOMAIN", "AUTH0_CLIENT_ID"]; | |
| // Also check VITE_ prefixed vars for react-spa | |
| const missing = required.filter((v) => { | |
| if (content.includes(v + "=")) return false; | |
| if (framework === "react-spa" && content.includes("VITE_" + v.replace("AUTH0_", "AUTH0_") + "=")) return false; | |
| if (framework === "react-spa" && content.includes(`VITE_${v}=`)) return false; | |
| return true; | |
| }); |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@plugins/auth0/skills/auth0-my-organization-api/skills/using-my-organization-api/scripts/verify-setup.mjs`
around lines 37 - 47, The script calls .includes() on the value returned by
readFile without guarding against a failed or undefined read; update the logic
around readFile and content so you first validate that content is a non-empty
string (or coerce to "") before running the required/filter checks (the
variables/functions to update are readFile, content, required, and the missing
filter), and if the file could not be read return a clear error or exit early
rather than performing .includes() on null/undefined.
|
|
||
| Detect the project state and follow this decision tree. Do not ask the user to choose a path. | ||
|
|
||
| ``` |
There was a problem hiding this comment.
Add fence languages to satisfy markdownlint and improve readability.
Several fenced blocks are missing a language identifier (MD040). Please add text, bash, json, etc., as appropriate at the listed lines.
Also applies to: 132-132, 260-260, 292-292, 623-623, 748-748, 759-759, 764-764
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)
[warning] 78-78: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@plugins/auth0/skills/auth0-my-organization-api/skills/using-my-organization-api/SKILL.md`
at line 78, Several fenced code blocks in SKILL.md are missing language
identifiers (violating MD040); update each triple-backtick block (the anonymous
``` fences) to include an appropriate language tag (e.g., ```bash, ```json,
```text) so markdownlint passes and readability improves—search for the unnamed
``` blocks in the SKILL.md content and replace them with language-tagged fences
that match the block contents.
Summary
Also covers backend validation, organization context middleware, and permission enforcement.
The skill ships with five supporting scripts under scripts/:
check is auto-fixed by the agent
Notable details
Test Plan
No automated regression harness in this PR. The skill has been manually exercised against the SaaStart path (empty repo → running dashboard), the porting path (existing Management API app), and the UI components path. A test harness is tracked as follow-up work.
Notes for Reviewers
By submitting a PR to this repository, you agree to the terms within the Auth0 Code of Conduct. Please see the contributing guidelines for how to create and submit a high-quality PR for this repo.