Skip to content

Commit

Permalink
feat(remix): Support handshake flow for remix (#2380)
Browse files Browse the repository at this point in the history
* feat(remix): Support handshake flow for remix

* chore(repo): Add changeset

* feat(remix): Support getAuth as well, consolidate handshake handling into authenticateRequest

* chore(remix): Add comment
  • Loading branch information
BRKalow committed Dec 15, 2023
1 parent 3b9e801 commit fa68746
Show file tree
Hide file tree
Showing 6 changed files with 49 additions and 21 deletions.
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

0 comments on commit fa68746

Please sign in to comment.