Skip to content
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(remix): Support handshake flow for remix #2380

Merged
merged 5 commits into from
Dec 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .changeset/itchy-chairs-call.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
'@clerk/remix': major
---

Update `@clerk/remix`'s `rootAuthLoader` and `getAuth` helpers to handle handshake auth status, this replaces the previous interstitial flow. As a result of this, the `ClerkErrorBoundary` is no longer necessary and has been removed.

To migrate, remove usage of `ClerkErrorBoundary`:

```diff
- import { ClerkApp, ClerkErrorBoundary } from "@clerk/remix";
+ import { ClerkApp } from "@clerk/remix";

...

- export const ErrorBoundary = ClerkErrorBoundary();
```
2 changes: 1 addition & 1 deletion packages/backend/src/internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,4 @@ export {
export { createIsomorphicRequest } from './util/IsomorphicRequest';

export { AuthStatus } from './tokens/authStatus';
export type { RequestState } from './tokens/authStatus';
export type { RequestState, SignedInState, SignedOutState } from './tokens/authStatus';
2 changes: 1 addition & 1 deletion packages/nextjs/src/server/authMiddleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ const authMiddleware: AuthMiddleware = (...args: unknown[]) => {
}

if (requestState.status === AuthStatus.Handshake) {
throw new Error('Unexpected handshake without redirect');
throw new Error('Clerk: unexpected handshake without redirect');
}

const auth = Object.assign(requestState.toAuth(), {
Expand Down
30 changes: 26 additions & 4 deletions packages/remix/src/ssr/authenticateRequest.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { createClerkClient } from '@clerk/backend';
import type { RequestState } from '@clerk/backend/internal';
import { buildRequestUrl } from '@clerk/backend/internal';
import type { SignedInState, SignedOutState } from '@clerk/backend/internal';
import { AuthStatus, buildRequestUrl } from '@clerk/backend/internal';
import { apiUrlFromPublishableKey } from '@clerk/shared/apiUrlFromPublishableKey';
import { handleValueOrFn } from '@clerk/shared/handleValueOrFn';
import { isDevelopmentFromSecretKey } from '@clerk/shared/keys';
Expand All @@ -11,7 +11,10 @@ import { noSecretKeyError, satelliteAndMissingProxyUrlAndDomain, satelliteAndMis
import { getEnvVariable } from '../utils';
import type { LoaderFunctionArgs, RootAuthLoaderOptions } from './types';

export function authenticateRequest(args: LoaderFunctionArgs, opts: RootAuthLoaderOptions = {}): Promise<RequestState> {
export async function authenticateRequest(
args: LoaderFunctionArgs,
opts: RootAuthLoaderOptions = {},
): Promise<SignedInState | SignedOutState> {
const { request, context } = args;
const { loadSession, loadUser, loadOrganization } = opts;
const { audience, authorizedParties } = opts;
Expand Down Expand Up @@ -71,7 +74,14 @@ export function authenticateRequest(args: LoaderFunctionArgs, opts: RootAuthLoad
throw new Error(satelliteAndMissingSignInUrl);
}

return createClerkClient({ apiUrl, secretKey, jwtKey, proxyUrl, isSatellite, domain }).authenticateRequest(request, {
const requestState = await createClerkClient({
apiUrl,
secretKey,
jwtKey,
proxyUrl,
isSatellite,
domain,
}).authenticateRequest(request, {
audience,
secretKey,
jwtKey,
Expand All @@ -88,4 +98,16 @@ export function authenticateRequest(args: LoaderFunctionArgs, opts: RootAuthLoad
afterSignInUrl,
afterSignUpUrl,
});

const hasLocationHeader = requestState.headers.get('location');
if (hasLocationHeader) {
// triggering a handshake redirect
throw new Response(null, { status: 307, headers: requestState.headers });
}

if (requestState.status === AuthStatus.Handshake) {
throw new Error('Clerk: unexpected handshake without redirect');
}

return requestState;
}
11 changes: 3 additions & 8 deletions packages/remix/src/ssr/getAuth.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { AuthStatus, sanitizeAuthObject } from '@clerk/backend/internal';
import { redirect } from '@remix-run/server-runtime';
import { sanitizeAuthObject } from '@clerk/backend/internal';

import { noLoaderArgsPassedInGetAuth } from '../errors';
import { authenticateRequest } from './authenticateRequest';
Expand All @@ -11,13 +10,9 @@ export async function getAuth(args: LoaderFunctionArgs, opts?: GetAuthOptions):
if (!args || (args && (!args.request || !args.context))) {
throw new Error(noLoaderArgsPassedInGetAuth);
}
const requestState = await authenticateRequest(args, opts);

// TODO handle handshake
// this halts the execution of all nested loaders using getAuth
if (requestState.status === AuthStatus.Handshake) {
throw redirect('');
}
// Note: authenticateRequest() will throw a redirect if the auth state is determined to be handshake
const requestState = await authenticateRequest(args, opts);

return sanitizeAuthObject(requestState.toAuth());
}
9 changes: 2 additions & 7 deletions packages/remix/src/ssr/rootAuthLoader.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { AuthStatus, sanitizeAuthObject } from '@clerk/backend/internal';
import { sanitizeAuthObject } from '@clerk/backend/internal';
import type { defer } from '@remix-run/server-runtime';
import { redirect } from '@remix-run/server-runtime';
import { isDeferredData } from '@remix-run/server-runtime/dist/responses';

import { invalidRootLoaderCallbackReturn } from '../errors';
Expand Down Expand Up @@ -48,13 +47,9 @@ export const rootAuthLoader: RootAuthLoader = async (
? handlerOrOptions
: {};

// Note: authenticateRequest() will throw a redirect if the auth state is determined to be handshake
const requestState = await authenticateRequest(args, opts);

// TODO handle handshake
if (requestState.status === AuthStatus.Handshake) {
throw redirect('');
}

if (!handler) {
// if the user did not provide a handler, simply inject requestState into an empty response
return injectRequestStateIntoResponse(new Response(JSON.stringify({})), requestState, args.context);
Expand Down
Loading