-
Notifications
You must be signed in to change notification settings - Fork 1.7k
feat(extension-auth): accept Firefox identity redirect hosts for extension auth #1999
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -20,7 +20,18 @@ import { handleDomainError } from "../Http/Errors.ts"; | |||||||||||||||||||||||
| import { Videos } from "../Videos/index.ts"; | ||||||||||||||||||||||||
| import { Extensions } from "./Extensions.ts"; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| const CHROMIUM_IDENTITY_HOST_SUFFIX = ".chromiumapp.org"; | ||||||||||||||||||||||||
| // Each browser's identity.launchWebAuthFlow intercepts redirects to its own | ||||||||||||||||||||||||
| // synthetic host; the leading label identifies the extension installation. | ||||||||||||||||||||||||
| const IDENTITY_REDIRECT_HOSTS = [ | ||||||||||||||||||||||||
| { | ||||||||||||||||||||||||
| suffix: ".chromiumapp.org", | ||||||||||||||||||||||||
| getConfiguredExtensionId: () => serverEnv().CAP_CHROME_EXTENSION_ID, | ||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||
| { | ||||||||||||||||||||||||
| suffix: ".extensions.allizom.org", | ||||||||||||||||||||||||
| getConfiguredExtensionId: () => serverEnv().CAP_FIREFOX_EXTENSION_ID, | ||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||
| ] as const; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| const validateExtensionRedirectUri = (redirectUri: string) => | ||||||||||||||||||||||||
| Effect.gen(function* () { | ||||||||||||||||||||||||
|
|
@@ -29,18 +40,15 @@ const validateExtensionRedirectUri = (redirectUri: string) => | |||||||||||||||||||||||
| catch: () => new HttpApiError.BadRequest(), | ||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| if ( | ||||||||||||||||||||||||
| url.protocol !== "https:" || | ||||||||||||||||||||||||
| !url.hostname.endsWith(CHROMIUM_IDENTITY_HOST_SUFFIX) | ||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||
| const identityHost = IDENTITY_REDIRECT_HOSTS.find(({ suffix }) => | ||||||||||||||||||||||||
| url.hostname.endsWith(suffix), | ||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||
| if (url.protocol !== "https:" || !identityHost) { | ||||||||||||||||||||||||
| return yield* new HttpApiError.BadRequest(); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| const extensionId = url.hostname.slice( | ||||||||||||||||||||||||
| 0, | ||||||||||||||||||||||||
| -CHROMIUM_IDENTITY_HOST_SUFFIX.length, | ||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||
| const configuredExtensionId = serverEnv().CAP_CHROME_EXTENSION_ID; | ||||||||||||||||||||||||
| const extensionId = url.hostname.slice(0, -identityHost.suffix.length); | ||||||||||||||||||||||||
| const configuredExtensionId = identityHost.getConfiguredExtensionId(); | ||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Prompt To Fix With AIThis is a comment left during a code review.
Path: packages/web-backend/src/Extension/Http.ts
Line: 51
Comment:
**Mixed-Case Firefox Id Rejection**
`URL.hostname` is normalized to lowercase before `extensionId` is sliced, but the new Firefox id comes straight from `CAP_FIREFOX_EXTENSION_ID`. If that env var is copied with uppercase characters, the configured-id check rejects the real Firefox redirect host and the extension auth flow always returns a bad request.
```suggestion
const configuredExtensionId = identityHost
.getConfiguredExtensionId()
?.toLowerCase();
```
How can I resolve this? If you propose a fix, please make it concise.
Comment on lines
+50
to
+51
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since the comment says the leading label identifies the extension installation, it could be good to explicitly reject multi-label hosts (e.g.
Suggested change
|
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| if (configuredExtensionId) { | ||||||||||||||||||||||||
| if (extensionId !== configuredExtensionId) { | ||||||||||||||||||||||||
|
|
@@ -53,7 +61,8 @@ const validateExtensionRedirectUri = (redirectUri: string) => | |||||||||||||||||||||||
| // signed-in user's auth key through this flow. The only deployment | ||||||||||||||||||||||||
| // where accepting an arbitrary id is safe is localhost development; | ||||||||||||||||||||||||
| // every reachable deployment (staging, previews, self-hosted) must set | ||||||||||||||||||||||||
| // CAP_CHROME_EXTENSION_ID regardless of NODE_ENV. | ||||||||||||||||||||||||
| // CAP_CHROME_EXTENSION_ID / CAP_FIREFOX_EXTENSION_ID regardless of | ||||||||||||||||||||||||
| // NODE_ENV. | ||||||||||||||||||||||||
| const webHostname = new URL(serverEnv().WEB_URL).hostname; | ||||||||||||||||||||||||
| const isLocalDevelopment = | ||||||||||||||||||||||||
| serverEnv().NODE_ENV !== "production" && | ||||||||||||||||||||||||
|
|
@@ -151,7 +160,7 @@ const renderConsentPage = ({ | |||||||||||||||||||||||
| </head> | ||||||||||||||||||||||||
| <body> | ||||||||||||||||||||||||
| <main class="card"> | ||||||||||||||||||||||||
| <h1>Connect the Cap Chrome extension</h1> | ||||||||||||||||||||||||
| <h1>Connect the Cap browser extension</h1> | ||||||||||||||||||||||||
| <p>The Cap extension is asking for access to your Cap account <span class="email">${escapeHtml(email)}</span> to create and upload recordings on your behalf.</p> | ||||||||||||||||||||||||
| <p>Only continue if you opened this page from the Cap extension.</p> | ||||||||||||||||||||||||
| <form method="post" action="approve" class="actions"> | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might be worth rejecting non-default ports / credentials here to keep the redirect URI shape tight (and avoid surprises if browser behavior changes).