-
Notifications
You must be signed in to change notification settings - Fork 327
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
feat(clerk-js): Authenticate with popup #5239
Conversation
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
🦋 Changeset detectedLatest commit: 367577b The changes in this PR will be included in the next version bump. This PR includes changesets to release 22 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
This comment was marked as outdated.
This comment was marked as outdated.
1cf0380
to
6b35b89
Compare
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
packages/clerk-js/src/core/clerk.ts
Outdated
let shouldRemoveListener = false; | ||
|
||
if (event.data.session) { | ||
const existingSession = this.client?.sessions.find(x => x.id === event.data.session) || null; |
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.
[nit] we are already checking for existence of this.client
on L1856 so no need for the optional chaining here
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.
I think it's because this is an async function that can run with delay which may result to this.client
not being defined.
packages/clerk-js/src/ui/components/SignIn/SignInSocialButtons.tsx
Outdated
Show resolved
Hide resolved
console.log('This can be safely ignored:'); | ||
console.error(err); |
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.
Is there a significant reason to leave the logs ?
packages/clerk-js/src/core/clerk.ts
Outdated
const r = new URL(redirectUrl); | ||
r.searchParams.set('sign_in_force_redirect_url', params.redirectUrlComplete); | ||
r.searchParams.set('sign_up_force_redirect_url', params.redirectUrlComplete); | ||
// All URLs are decorated with the dev browser token in development mode since we're moving between AP and the app. |
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.
✅
packages/clerk-js/src/core/clerk.ts
Outdated
let shouldRemoveListener = false; | ||
|
||
if (event.data.session) { | ||
const existingSession = this.client?.sessions.find(x => x.id === event.data.session) || null; |
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.
I think it's because this is an async function that can run with delay which may result to this.client
not being defined.
packages/clerk-js/src/core/clerk.ts
Outdated
if (!existingSession) { | ||
try { | ||
await this.client?.reload(); | ||
} catch (e) { | ||
console.error(e); | ||
} | ||
} |
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.
Is this another attempt to recover if stale ?
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.
Yup, I noticed in a few instances we were receiving a session ID that wasn't in the return value from this.client.session
.
packages/clerk-js/src/core/clerk.ts
Outdated
await this.setActive({ | ||
session: event.data.session, | ||
redirectUrl: params.redirectUrlComplete, | ||
}); |
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.
Is it on purpose that we existingSession
may not exist, but we are still calling set active with the event data that was provided ?
const POPUP_PREFERRED_ORIGINS = ['.lovable.app', '.lovableproject.com', '.webcontainer-api.io']; | ||
|
||
/** | ||
* Returns `true` if the current origin is one that is typically embedded via an iframe, which would benefit from the | ||
* popup flow. | ||
* @returns {boolean} Whether the current origin prefers the popup flow. | ||
*/ | ||
export function originPrefersPopup(): boolean { | ||
return POPUP_PREFERRED_ORIGINS.some(origin => window.location.origin.endsWith(origin)); | ||
} |
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.
potentially something that could be fetched from /environment
, but this feels ok as well.
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.
Your suggesting we set the POPUP_PREFERRED_DOMAINS
on the backend? (if so, I do kind of like that doing it from there would give us the ability to modify without releasing - I guess our hotloading kind of gives us the same capability though)
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.
Agreed, that's why I don't feel strongly about it. Only someone with a pinned version would not benefit from hotloading, but I assume users of these tools won't have a reason to do that.
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.
Yup, Bryce and I discussed this. Ultimately we also concurred that it's likely not that big of a deal where this logic lives. There was some hesitancy from my side on whether it's easier to cut a clerk-js
release versus updating the return value from a FAPI endpoint.
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.
We're also maintaining a very similar list of origins for AP frame ancestors.
Not blocking for this PR, but I'll capture the "store and reference in a central place" as an enhancement.
packages/clerk-js/src/ui/components/SignIn/SignInSocialButtons.tsx
Outdated
Show resolved
Hide resolved
|
||
import type { Clerk } from '../core/clerk'; | ||
|
||
export async function _authenticateWithPopup( |
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.
Curious why we are exporting a function with a leading _
?
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.
In our SignIn
and SignUp
resources, we have a public authenticateWithPopup
method available at signIn.authenticateWithPopup
. These methods share code, so that's pulled out into the shared _authenticateWithPopup
method. I would love a better name if you have any suggestions, but the underscore is to distinguish it from the actual public method.
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.
No major concerns
Description
This PR implements the ability to authenticate with a popup instead of a redirect for external connections. This is useful in environments like Loveable and Bolt where we're unable to render pages like Google inside of an iframe.
It accomplishes this by adding a new prop to
SignIn
andSignUp
calledoauthFlow
, with the values"redirect" | "popup" | "auto"
. The"auto"
value uses a new utility,originPrefersPopup
, to determine whether the given origin is one that likely needs to use the popup flow. This means that generative AI coding tools do not need to make any specific changes to their code for OAuth flows to work as expected. Users are, of course, able to opt into the"popup"
flow specifically if they desire, however we maintain that the"redirect"
option is a better UX.Checklist
pnpm test
runs as expected.pnpm build
runs as expected.Type of change