diff --git a/.changeset/short-otters-return.md b/.changeset/short-otters-return.md
new file mode 100644
index 00000000000..3e00c937766
--- /dev/null
+++ b/.changeset/short-otters-return.md
@@ -0,0 +1,5 @@
+---
+"@clerk/astro": patch
+---
+
+Introduce `` as an Astro and as a React component
diff --git a/packages/astro/src/astro-components/control/AuthenticateWithRedirectCallback.astro b/packages/astro/src/astro-components/control/AuthenticateWithRedirectCallback.astro
new file mode 100644
index 00000000000..713cf3117c4
--- /dev/null
+++ b/packages/astro/src/astro-components/control/AuthenticateWithRedirectCallback.astro
@@ -0,0 +1,42 @@
+---
+import type { HandleOAuthCallbackParams } from '@clerk/types';
+
+type Props = HandleOAuthCallbackParams
+
+import { generateSafeId } from '@clerk/astro/internal';
+
+const safeId = generateSafeId();
+
+// TODO: Move this to a prop when we implement more control components.
+const functionName = 'handleRedirectCallback';
+---
+
+
+
+
diff --git a/packages/astro/src/astro-components/index.ts b/packages/astro/src/astro-components/index.ts
index fa816b7b52c..f9714851fbd 100644
--- a/packages/astro/src/astro-components/index.ts
+++ b/packages/astro/src/astro-components/index.ts
@@ -4,6 +4,7 @@
export { default as SignedIn } from './control/SignedIn.astro';
export { default as SignedOut } from './control/SignedOut.astro';
export { default as Protect } from './control/Protect.astro';
+export { default as AuthenticateWithRedirectCallback } from './control/AuthenticateWithRedirectCallback.astro';
/**
* Unstyled Components
diff --git a/packages/astro/src/internal/create-clerk-instance.ts b/packages/astro/src/internal/create-clerk-instance.ts
index a3442ac23a5..ed50b90e4bd 100644
--- a/packages/astro/src/internal/create-clerk-instance.ts
+++ b/packages/astro/src/internal/create-clerk-instance.ts
@@ -1,6 +1,7 @@
import { waitForClerkScript } from '../internal/utils/loadClerkJSScript';
import { $clerk, $csrState } from '../stores/internal';
import type { AstroClerkIntegrationParams, AstroClerkUpdateOptions } from '../types';
+import { invokeClerkAstroJSFunctions } from './invoke-clerk-astro-js-functions';
import { mountAllClerkAstroJSComponents } from './mount-clerk-astro-js-components';
import { runOnce } from './run-once';
@@ -35,6 +36,7 @@ async function createClerkInstanceInternal(options?: AstroClerkIntegrationParams
$csrState.setKey('isLoaded', true);
mountAllClerkAstroJSComponents();
+ invokeClerkAstroJSFunctions();
clerkJSInstance.addListener(payload => {
$csrState.setKey('client', payload.client);
diff --git a/packages/astro/src/internal/invoke-clerk-astro-js-functions.ts b/packages/astro/src/internal/invoke-clerk-astro-js-functions.ts
new file mode 100644
index 00000000000..92e1e95635f
--- /dev/null
+++ b/packages/astro/src/internal/invoke-clerk-astro-js-functions.ts
@@ -0,0 +1,19 @@
+import { $clerk } from '../stores/internal';
+
+/**
+ * Loop through any Astro component that has requested to invoke a function and invoke it with its respective props.
+ */
+const invokeClerkAstroJSFunctions = () => {
+ const functionNames = ['handleRedirectCallback'] as const;
+
+ functionNames.forEach(fnName => {
+ const elementsOfCategory = document.querySelectorAll(`[data-clerk-function-id^="clerk-${fnName}"]`);
+ elementsOfCategory.forEach(el => {
+ const id = el.getAttribute('data-clerk-function-id');
+ const props = window.__astro_clerk_function_props?.get(fnName)?.get(id!) ?? {};
+ void $clerk.get()?.[fnName]?.(props);
+ });
+ });
+};
+
+export { invokeClerkAstroJSFunctions };
diff --git a/packages/astro/src/internal/run-once.ts b/packages/astro/src/internal/run-once.ts
index 11ec81768d3..f36149f9c2f 100644
--- a/packages/astro/src/internal/run-once.ts
+++ b/packages/astro/src/internal/run-once.ts
@@ -1,5 +1,6 @@
import type { Clerk } from '@clerk/clerk-js';
+import { invokeClerkAstroJSFunctions } from './invoke-clerk-astro-js-functions';
import { mountAllClerkAstroJSComponents } from './mount-clerk-astro-js-components';
import type { CreateClerkInstanceInternalFn } from './types';
@@ -19,6 +20,7 @@ const runOnce = (onFirst: CreateClerkInstanceInternalFn) => {
if (clerkJSInstance.loaded) {
mountAllClerkAstroJSComponents();
+ invokeClerkAstroJSFunctions();
}
return res(clerkJSInstance.loaded);
});
diff --git a/packages/astro/src/react/controlComponents.tsx b/packages/astro/src/react/controlComponents.tsx
index 71c2ed41dad..6e17605f109 100644
--- a/packages/astro/src/react/controlComponents.tsx
+++ b/packages/astro/src/react/controlComponents.tsx
@@ -1,8 +1,11 @@
-import type { CheckAuthorizationWithCustomPermissions } from '@clerk/types';
+import type { CheckAuthorizationWithCustomPermissions, HandleOAuthCallbackParams } from '@clerk/types';
import type { PropsWithChildren } from 'react';
+import React from 'react';
import type { ProtectComponentDefaultProps } from '../types';
import { useAuth } from './hooks';
+import type { WithClerkProp } from './utils';
+import { withClerk } from './utils';
export function SignedOut(props: PropsWithChildren) {
const { userId } = useAuth();
@@ -83,3 +86,17 @@ export const Protect = ({ children, fallback, ...restAuthorizedParams }: Protect
*/
return authorized;
};
+
+/**
+ * Use `` to complete a custom OAuth flow.
+ */
+export const AuthenticateWithRedirectCallback = withClerk(
+ ({ clerk, ...handleRedirectCallbackParams }: WithClerkProp) => {
+ React.useEffect(() => {
+ void clerk?.handleRedirectCallback(handleRedirectCallbackParams);
+ }, []);
+
+ return null;
+ },
+ 'AuthenticateWithRedirectCallback',
+);
diff --git a/packages/astro/src/types.ts b/packages/astro/src/types.ts
index c68c878b432..935d3d4c7b6 100644
--- a/packages/astro/src/types.ts
+++ b/packages/astro/src/types.ts
@@ -20,6 +20,7 @@ type AstroClerkCreateInstanceParams = AstroClerkIntegrationParams & { publishabl
declare global {
interface Window {
__astro_clerk_component_props: Map>>;
+ __astro_clerk_function_props: Map>>;
}
}