Skip to content

Commit

Permalink
chore(backend): Enforce request param in authenticateRequest (#2122)
Browse files Browse the repository at this point in the history
* chore(backend): Drop unused `RequiredVerifyTokenOptions` type

* chore(backend): Restructure AuthStatus/AuthObject/InterstitialRule types dependencies

Instead of passing the `AuthenticateRequestOptions` down the dependency tree
`authenticateRequest => interstitialRule => AuthStatus => AuthObjects`, we
change the approach to bottom-up by definiting types per layer and importing them
to the parent layer. Also options of parent layers extend the options of child layers.

* feat(backend): Enforce `request` param in authenticateRequest - replace header related options

* chore(backend): Drop usage of x-forwarded-port

* chore(repo): Add changeset
  • Loading branch information
dimkl committed Nov 16, 2023
1 parent 5471c7e commit 9615e6c
Show file tree
Hide file tree
Showing 11 changed files with 396 additions and 260 deletions.
57 changes: 57 additions & 0 deletions .changeset/red-coats-itch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
---
'@clerk/backend': major
---

Enforce passing `request` param to `authenticateRequest` method of `@clerk/backend`
instead of passing each header or cookie related option that is used internally to
determine the request state.

Migration guide:
- use `request` param in `clerkClient.authenticateRequest()` instead of:
- `origin`
- `host`
- `forwardedHost`
- `forwardedProto`
- `referrer`
- `userAgent`
- `cookieToken`
- `clientUat`
- `headerToken`
- `searchParams`

Example
```typescript
//
// current
//
import { clerkClient } from '@clerk/backend'

const requestState = await clerkClient.authenticateRequest({
secretKey: 'sk_....'
publishableKey: 'pk_....'
origin: req.headers.get('origin'),
host: req.headers.get('host'),
forwardedHost: req.headers.get('x-forwarded-host'),
forwardedProto: req.headers.get('x-forwarded-proto'),
referrer: req.headers.get('referer'),
userAgent: req.headers.get('user-agent'),
clientUat: req.cookies.get('__client_uat'),
cookieToken: req.cookies.get('__session'),
headerToken: req.headers.get('authorization'),
searchParams: req.searchParams
});

//
// new
//
import { clerkClient, } from '@clerk/backend'

// use req (if it's a fetch#Request instance) or use `createIsomorphicRequest` from `@clerk/backend`
// to re-construct fetch#Request instance
const requestState = await clerkClient.authenticateRequest({
secretKey: 'sk_....'
publishableKey: 'pk_....'
request: req
});

```
11 changes: 1 addition & 10 deletions packages/backend/src/api/factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,7 @@ import {
} from './endpoints';
import { buildRequest } from './request';

export type CreateBackendApiOptions = {
/* Secret Key */
secretKey?: string;
/* Backend API URL */
apiUrl?: string;
/* Backend API version */
apiVersion?: string;
/* Library/SDK name */
userAgent?: string;
};
export type CreateBackendApiOptions = Parameters<typeof buildRequest>[0];

export type ApiClient = ReturnType<typeof createBackendApiClient>;

Expand Down
14 changes: 12 additions & 2 deletions packages/backend/src/api/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import { API_URL, API_VERSION, constants, USER_AGENT } from '../constants';
import runtime from '../runtime';
import { assertValidSecretKey } from '../util/assertValidSecretKey';
import { joinPaths } from '../util/path';
import type { CreateBackendApiOptions } from './factory';
import { deserialize } from './resources/Deserializer';

export type ClerkBackendApiRequestOptions = {
Expand Down Expand Up @@ -64,7 +63,18 @@ const withLegacyReturn =
}
};

export function buildRequest(options: CreateBackendApiOptions) {
type BuildRequestOptions = {
/* Secret Key */
secretKey?: string;
/* Backend API URL */
apiUrl?: string;
/* Backend API version */
apiVersion?: string;
/* Library/SDK name */
userAgent?: string;
};

export function buildRequest(options: BuildRequestOptions) {
const request = async <T>(requestOptions: ClerkBackendApiRequestOptions): Promise<ClerkBackendApiResponse<T>> => {
const { secretKey, apiUrl = API_URL, apiVersion = API_VERSION, userAgent = USER_AGENT } = options;
const { path, method, queryParams, headerParams, bodyParams, formData } = requestOptions;
Expand Down
13 changes: 4 additions & 9 deletions packages/backend/src/tokens/authObjects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,14 @@ import type {
ServerGetTokenOptions,
} from '@clerk/types';

import type { Organization, Session, User } from '../api';
import type { CreateBackendApiOptions, Organization, Session, User } from '../api';
import { createBackendApiClient } from '../api';
import type { RequestState } from './authStatus';
import type { AuthenticateRequestOptions } from './request';

type AuthObjectDebugData = Partial<AuthenticateRequestOptions & RequestState>;
type AuthObjectDebugData = Record<string, any>;
type CreateAuthObjectDebug = (data?: AuthObjectDebugData) => AuthObjectDebug;
type AuthObjectDebug = () => unknown;
type AuthObjectDebug = () => AuthObjectDebugData;

export type SignedInAuthObjectOptions = {
secretKey?: string;
apiUrl: string;
apiVersion: string;
export type SignedInAuthObjectOptions = CreateBackendApiOptions & {
token: string;
session?: Session;
user?: User;
Expand Down
109 changes: 89 additions & 20 deletions packages/backend/src/tokens/authStatus.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { JwtPayload } from '@clerk/types';

import { createBackendApiClient } from '../api';
import type { SignedInAuthObject, SignedOutAuthObject } from './authObjects';
import type { SignedInAuthObject, SignedInAuthObjectOptions, SignedOutAuthObject } from './authObjects';
import { signedInAuthObject, signedOutAuthObject } from './authObjects';
import type { TokenVerificationErrorReason } from './errors';

Expand Down Expand Up @@ -81,25 +81,59 @@ export type AuthReason = AuthErrorReason | TokenVerificationErrorReason;

export type RequestState = SignedInState | SignedOutState | InterstitialState | UnknownState;

export async function signedIn<T>(options: T, sessionClaims: JwtPayload): Promise<SignedInState> {
export type LoadResourcesOptions = {
loadSession?: boolean;
loadUser?: boolean;
loadOrganization?: boolean;
};

type RequestStateParams = {
publishableKey?: string;
domain?: string;
isSatellite?: boolean;
proxyUrl?: string;
signInUrl?: string;
signUpUrl?: string;
afterSignInUrl?: string;
afterSignUpUrl?: string;
};

type AuthParams = {
/* Client token cookie value */
cookieToken?: string;
/* Client uat cookie value */
clientUat?: string;
/* Client token header value */
headerToken?: string;
};

export type AuthStatusOptionsType = LoadResourcesOptions &
Partial<SignedInAuthObjectOptions> &
RequestStateParams &
AuthParams;

export async function signedIn<T extends AuthStatusOptionsType>(
options: T,
sessionClaims: JwtPayload,
): Promise<SignedInState> {
const {
publishableKey = '',
proxyUrl = '',
isSatellite = false,
domain = '',
signInUrl = '',
signUpUrl = '',
afterSignInUrl = '',
afterSignUpUrl = '',
secretKey,
apiUrl,
apiVersion,
cookieToken,
proxyUrl,
publishableKey,
domain,
isSatellite,
headerToken,
loadSession,
loadUser,
loadOrganization,
signInUrl,
signUpUrl,
afterSignInUrl,
afterSignUpUrl,
} = options as any;
} = options;

const { sid: sessionId, org_id: orgId, sub: userId } = sessionClaims;

Expand Down Expand Up @@ -154,10 +188,21 @@ export async function signedIn<T>(options: T, sessionClaims: JwtPayload): Promis
toAuth: () => authObject,
};
}

export function signedOut<T>(options: T, reason: AuthReason, message = ''): SignedOutState {
const { publishableKey, proxyUrl, isSatellite, domain, signInUrl, signUpUrl, afterSignInUrl, afterSignUpUrl } =
options as any;
export function signedOut<T extends AuthStatusOptionsType>(
options: T,
reason: AuthReason,
message = '',
): SignedOutState {
const {
publishableKey = '',
proxyUrl = '',
isSatellite = false,
domain = '',
signInUrl = '',
signUpUrl = '',
afterSignInUrl = '',
afterSignUpUrl = '',
} = options;

return {
status: AuthStatus.SignedOut,
Expand All @@ -178,9 +223,22 @@ export function signedOut<T>(options: T, reason: AuthReason, message = ''): Sign
};
}

export function interstitial<T>(options: T, reason: AuthReason, message = ''): InterstitialState {
const { publishableKey, proxyUrl, isSatellite, domain, signInUrl, signUpUrl, afterSignInUrl, afterSignUpUrl } =
options as any;
export function interstitial<T extends AuthStatusOptionsType>(
options: T,
reason: AuthReason,
message = '',
): InterstitialState {
const {
publishableKey = '',
proxyUrl = '',
isSatellite = false,
domain = '',
signInUrl = '',
signUpUrl = '',
afterSignInUrl = '',
afterSignUpUrl = '',
} = options;

return {
status: AuthStatus.Interstitial,
reason,
Expand All @@ -200,15 +258,26 @@ export function interstitial<T>(options: T, reason: AuthReason, message = ''): I
};
}

export function unknownState<T>(options: T, reason: AuthReason, message = ''): UnknownState {
const { publishableKey, isSatellite, domain, signInUrl, signUpUrl, afterSignInUrl, afterSignUpUrl } = options as any;
export function unknownState(options: AuthStatusOptionsType, reason: AuthReason, message = ''): UnknownState {
const {
publishableKey = '',
proxyUrl = '',
isSatellite = false,
domain = '',
signInUrl = '',
signUpUrl = '',
afterSignInUrl = '',
afterSignUpUrl = '',
} = options;

return {
status: AuthStatus.Unknown,
reason,
message,
publishableKey,
isSatellite,
domain,
proxyUrl,
signInUrl,
signUpUrl,
afterSignInUrl,
Expand Down
6 changes: 0 additions & 6 deletions packages/backend/src/tokens/factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ export type CreateAuthenticateRequestOptions = {
| 'proxyUrl'
| 'domain'
| 'isSatellite'
| 'userAgent'
>
>;
apiClient: ApiClient;
Expand All @@ -36,7 +35,6 @@ export function createAuthenticateRequest(params: CreateAuthenticateRequestOptio
isSatellite: buildtimeIsSatellite = false,
domain: buildtimeDomain = '',
audience: buildtimeAudience = '',
userAgent: buildtimeUserAgent,
} = params.options;

const authenticateRequest = ({
Expand All @@ -47,8 +45,6 @@ export function createAuthenticateRequest(params: CreateAuthenticateRequestOptio
jwtKey: runtimeJwtKey,
isSatellite: runtimeIsSatellite,
domain: runtimeDomain,
searchParams: runtimeSearchParams,
userAgent: runtimeUserAgent,
...rest
}: Omit<AuthenticateRequestOptions, 'apiUrl' | 'apiVersion'>) => {
return authenticateRequestOriginal({
Expand All @@ -62,8 +58,6 @@ export function createAuthenticateRequest(params: CreateAuthenticateRequestOptio
isSatellite: runtimeIsSatellite || buildtimeIsSatellite,
domain: runtimeDomain || buildtimeDomain,
jwtKey: runtimeJwtKey || buildtimeJwtKey,
searchParams: runtimeSearchParams,
userAgent: runtimeUserAgent?.toString() || buildtimeUserAgent,
});
};

Expand Down
2 changes: 1 addition & 1 deletion packages/backend/src/tokens/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ export * from './errors';
export * from './factory';
export { loadInterstitialFromLocal } from './interstitial';
export { debugRequestState } from './request';
export type { AuthenticateRequestOptions, OptionalVerifyTokenOptions, RequiredVerifyTokenOptions } from './request';
export type { AuthenticateRequestOptions, OptionalVerifyTokenOptions } from './request';

0 comments on commit 9615e6c

Please sign in to comment.