Not affiliated with Warmbly
++ {info.self_registered + ? "This app registered itself and chose its own name, so Warmbly cannot say who made it." + : "Someone outside Warmbly made this app, and Warmbly has not reviewed it."}{" "} + Approve only if you started this connection yourself. +
+- You'll be redirected to {(() => { - try { - return new URL(info.redirect_uri).host; - } catch { - return "the app"; - } - })()}. Only authorize apps you trust. + Authorizing sends access to {redirectDestination(info.redirect_uri)}. Only + authorize apps you trust.
); diff --git a/web/src/lib/api/models/app/oauth/OAuthApp.ts b/web/src/lib/api/models/app/oauth/OAuthApp.ts index 761766609..71c7b876c 100644 --- a/web/src/lib/api/models/app/oauth/OAuthApp.ts +++ b/web/src/lib/api/models/app/oauth/OAuthApp.ts @@ -71,6 +71,8 @@ export interface OAuthConsentInfo { organization_name: string; // A registered app the instance features in its directory. verified: boolean; + // Registered itself (RFC 7591), so nobody vouches for its name. + self_registered: boolean; } // An app the current user has authorized (GET /oauth/authorized-apps). diff --git a/web/src/lib/displayName.test.ts b/web/src/lib/displayName.test.ts index 316ee759a..21b7c24cf 100644 --- a/web/src/lib/displayName.test.ts +++ b/web/src/lib/displayName.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "vitest"; -import { nameError } from "./displayName"; +import { appNameError, nameError } from "./displayName"; describe("nameError", () => { it.each(["Ada", " Mary Ann ", "O'Brien-Smith", "J.R.R. Tolkien", "St. John", "Zoë", "李小龙"])( @@ -39,3 +39,17 @@ describe("nameError", () => { expect(nameError("Name", "3000", "person")).toMatch(/letter/); }); }); + +describe("appNameError", () => { + it.each(["Warmbly", "Warmbly Support", "HubSpot for warmbly", "W a r m b l y", "WarmbIy", "Warrnbly", "VVarmbly", "Wаrmbly", "Warmbly"])( + "refuses %s", + (v) => expect(appNameError("Name", v)).toMatch(/cannot include Warmbly/), + ); + + it.each(["Acme Sync", "Warm Leads", "Swarm"])("accepts %s", (v) => expect(appNameError("Name", v)).toBeNull()); + + it("keeps an existing name editable", () => { + expect(appNameError("Name", "Warmbly Sync", "Warmbly Sync")).toBeNull(); + expect(appNameError("Name", "Warmbly Sync 2", "Warmbly Sync")).toMatch(/cannot include Warmbly/); + }); +}); diff --git a/web/src/lib/displayName.ts b/web/src/lib/displayName.ts index 741c3c853..af40985fa 100644 --- a/web/src/lib/displayName.ts +++ b/web/src/lib/displayName.ts @@ -45,3 +45,30 @@ export function nameError(label: string, value: string, kind: NameKind, optional } return null; } + +// Mirrors mentionsPlatform in internal/app/oauth/naming.go. +const CONFUSABLES: Record