Skip to content

feat(nextjs): Introduce machine authentication #5710

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

Merged
merged 72 commits into from
May 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
c1d4902
feat(backend): Add machine authentication support
wobsoriano Apr 22, 2025
1837b08
chore: re-export auth object types and functions
wobsoriano Apr 22, 2025
fbf2b53
test: add machine token location tests
wobsoriano Apr 22, 2025
2d0f910
chore: Update OAuth prefix
wobsoriano Apr 22, 2025
a3a3d03
chore: add scopes to machine auth tokens
wobsoriano Apr 23, 2025
0f6bd46
chore: switch to interface overloads
wobsoriano Apr 24, 2025
63f264f
chore: separate any token type and invidual with examples
wobsoriano Apr 24, 2025
e677546
chore: export verifyMachineAuthToken internally
wobsoriano Apr 24, 2025
45f26e2
chore: rename NonSessionTokenType to something more direct
wobsoriano Apr 25, 2025
7515994
chore: drop claims from oauth access token
wobsoriano Apr 25, 2025
f75ebb9
chore: Allow custom properties per machine token type
wobsoriano Apr 25, 2025
fc5b0e5
chore: type clean up and export machine utilities
wobsoriano Apr 25, 2025
bd4117d
chore: clean up tests
wobsoriano Apr 25, 2025
0de35ee
chore: Add default version for machine auth urls
wobsoriano Apr 25, 2025
0e3d527
chore: clean up more tests
wobsoriano Apr 25, 2025
2c7b3ad
Merge branch 'main' into rob/robo-36-sdk-m2m
wobsoriano Apr 25, 2025
3069da6
feat(nextjs): Add machine auth support
wobsoriano Apr 22, 2025
0e0d0f3
chore: accept any token and guard using protect
wobsoriano Apr 23, 2025
ddb52ec
chore: Make protect accept an array of tokens
wobsoriano Apr 23, 2025
d34443c
chore: start with interface overloading
wobsoriano Apr 24, 2025
13c6be0
chore: Improve types
wobsoriano Apr 24, 2025
39539fd
chore: reuse auth function types in middleware
wobsoriano Apr 24, 2025
fab6cee
chore: add examples to types
wobsoriano Apr 24, 2025
7ad5f0e
chore: handle auth token types in middleware
wobsoriano Apr 25, 2025
77ca6ac
chore: initial leaf node auth access
wobsoriano Apr 25, 2025
8d468d2
chore: clean up and reuse token type checker utilities
wobsoriano Apr 25, 2025
f4be731
chore: reuse token type utility from backend
wobsoriano Apr 25, 2025
050a03a
chore: check auth handler options
wobsoriano Apr 25, 2025
fa67f84
test: accommodate machine auth changes
wobsoriano Apr 25, 2025
65ee2be
chore: allow only session tokens in sync version of getAuthDataFromRe…
wobsoriano Apr 25, 2025
ebee0e2
chore: run format
wobsoriano Apr 28, 2025
25c0357
Merge branch 'main' into rob/robo-36-sdk-m2m
wobsoriano Apr 28, 2025
3fc232c
chore: fix auth object types
wobsoriano Apr 28, 2025
0c36065
chore: adjust nuxt types
wobsoriano Apr 28, 2025
d9d39cd
chore: Update other SDKs to conform with updated AuthObject
wobsoriano Apr 28, 2025
bd50d67
Merge branch 'rob/robo-36-sdk-m2m' into rob/robo-114-nextjs-sdk-m2m
wobsoriano Apr 28, 2025
d1297bf
chore: fix nuxt types
wobsoriano Apr 28, 2025
b2adc2a
Merge branch 'rob/robo-36-sdk-m2m' into rob/robo-114-nextjs-sdk-m2m
wobsoriano Apr 28, 2025
309a7e3
chore: export missing type
wobsoriano Apr 28, 2025
b3c4e4d
test: add middleware tests
wobsoriano Apr 28, 2025
67e3b57
test: add auth() verification tests
wobsoriano Apr 28, 2025
98ef24e
chore: introduce TokenType const
wobsoriano Apr 28, 2025
62b82a5
Merge branch 'rob/robo-36-sdk-m2m' into rob/robo-114-nextjs-sdk-m2m
wobsoriano Apr 28, 2025
32acde8
chore: fix incorrect import
wobsoriano Apr 28, 2025
aaa36ad
Merge branch 'rob/robo-36-sdk-m2m' into rob/robo-114-nextjs-sdk-m2m
wobsoriano Apr 28, 2025
fb60bff
chore: export other token types
wobsoriano Apr 29, 2025
7c46406
Merge branch 'rob/robo-36-sdk-m2m' into rob/robo-114-nextjs-sdk-m2m
wobsoriano Apr 29, 2025
b84424a
chore: use exported token types
wobsoriano Apr 29, 2025
5104bfa
chore: add comment on why we skip overloads in some places
wobsoriano Apr 29, 2025
db13acb
chore: add comments
wobsoriano Apr 29, 2025
d28a33b
chore: add remark about token prefixes
wobsoriano Apr 29, 2025
bd19a12
Merge branch 'rob/robo-36-sdk-m2m' into rob/robo-114-nextjs-sdk-m2m
wobsoriano Apr 29, 2025
86f4783
test: use token type consts in tests
wobsoriano Apr 29, 2025
7f656c4
chore: update doc regarding versioning
wobsoriano Apr 29, 2025
80ebf84
chore: update doc regarding versioning
wobsoriano Apr 29, 2025
efecb37
Merge branch 'main' into rob/robo-36-sdk-m2m
wobsoriano Apr 30, 2025
fd115bd
Merge branch 'rob/robo-36-sdk-m2m' into rob/robo-114-nextjs-sdk-m2m
wobsoriano Apr 30, 2025
40e467f
chore: return 401 response to token mismatch and unauthenticated mach…
wobsoriano Apr 30, 2025
553a989
chore: set www-authenticate header for oauth_token requests
wobsoriano Apr 30, 2025
ef16956
chore: Update www-authenticate header to use fapi
wobsoriano Apr 30, 2025
cac0361
Update getAuthDataFromRequest.ts
wobsoriano May 1, 2025
26ea907
chore: update to use new token prefixes
wobsoriano May 1, 2025
b6b744a
Merge branch 'rob/robo-36-sdk-m2m' into rob/robo-114-nextjs-sdk-m2m
wobsoriano May 1, 2025
b74befd
chore: accommodate new prefixes
wobsoriano May 1, 2025
f5b1064
Merge branch 'rob/robo-36-sdk-m2m' into rob/robo-114-nextjs-sdk-m2m
wobsoriano May 1, 2025
b41d744
chore: resolve conflicts
wobsoriano May 1, 2025
a6676e7
Merge branch 'rob/robo-36-sdk-m2m' into rob/robo-114-nextjs-sdk-m2m
wobsoriano May 1, 2025
cf3fbe8
chore: run format
wobsoriano May 1, 2025
138d2ed
Merge branch 'rob/robo-36-sdk-m2m' into rob/robo-114-nextjs-sdk-m2m
wobsoriano May 1, 2025
7cff045
chore: run format
wobsoriano May 1, 2025
26a3b5d
Merge branch 'rob/robo-36-sdk-m2m' into rob/robo-114-nextjs-sdk-m2m
wobsoriano May 5, 2025
1fc468b
chore: test actual output of www-authenticate header
wobsoriano May 6, 2025
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
66 changes: 56 additions & 10 deletions packages/nextjs/src/app-router/server/auth.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,31 @@
import type { RedirectFun, SignedInAuthObject, SignedOutAuthObject } from '@clerk/backend/internal';
import { constants, createClerkRequest, createRedirect } from '@clerk/backend/internal';
import type { AuthObject } from '@clerk/backend';
import type {
AuthenticatedMachineObject,
AuthenticateRequestOptions,
RedirectFun,
SignedInAuthObject,
SignedOutAuthObject,
UnauthenticatedMachineObject,
} from '@clerk/backend/internal';
import { constants, createClerkRequest, createRedirect, TokenType } from '@clerk/backend/internal';
import { notFound, redirect } from 'next/navigation';

import { PUBLISHABLE_KEY, SIGN_IN_URL, SIGN_UP_URL } from '../../server/constants';
import { createAsyncGetAuth } from '../../server/createGetAuth';
import { authAuthHeaderMissing } from '../../server/errors';
import { getAuthKeyFromRequest, getHeader } from '../../server/headers-utils';
import { unauthorized } from '../../server/nextErrors';
import type { AuthProtect } from '../../server/protect';
import { createProtect } from '../../server/protect';
import type { InferAuthObjectFromToken, InferAuthObjectFromTokenArray } from '../../server/types';
import { decryptClerkRequestData } from '../../server/utils';
import { isNextWithUnstableServerActions } from '../../utils/sdk-versions';
import { buildRequestLike } from './utils';

/**
* `Auth` object of the currently active user and the `redirectToSignIn()` method.
*/
type Auth = (SignedInAuthObject | SignedOutAuthObject) & {
type SessionAuth<TRedirect> = (SignedInAuthObject | SignedOutAuthObject) & {
/**
* The `auth()` helper returns the `redirectToSignIn()` method, which you can use to redirect the user to the sign-in page.
*
Expand All @@ -24,7 +34,7 @@ type Auth = (SignedInAuthObject | SignedOutAuthObject) & {
* > [!NOTE]
* > `auth()` on the server-side can only access redirect URLs defined via [environment variables](https://clerk.com/docs/deployments/clerk-environment-variables#sign-in-and-sign-up-redirects) or [`clerkMiddleware` dynamic keys](https://clerk.com/docs/references/nextjs/clerk-middleware#dynamic-keys).
*/
redirectToSignIn: RedirectFun<ReturnType<typeof redirect>>;
redirectToSignIn: RedirectFun<TRedirect>;

/**
* The `auth()` helper returns the `redirectToSignUp()` method, which you can use to redirect the user to the sign-up page.
Expand All @@ -34,11 +44,44 @@ type Auth = (SignedInAuthObject | SignedOutAuthObject) & {
* > [!NOTE]
* > `auth()` on the server-side can only access redirect URLs defined via [environment variables](https://clerk.com/docs/deployments/clerk-environment-variables#sign-in-and-sign-up-redirects) or [`clerkMiddleware` dynamic keys](https://clerk.com/docs/references/nextjs/clerk-middleware#dynamic-keys).
*/
redirectToSignUp: RedirectFun<ReturnType<typeof redirect>>;
redirectToSignUp: RedirectFun<TRedirect>;
};

export interface AuthFn {
(): Promise<Auth>;
// Machine token auth objects
type MachineAuth<T extends TokenType> = (AuthenticatedMachineObject | UnauthenticatedMachineObject) & {
tokenType: T;
};

export type AuthOptions = { acceptsToken?: AuthenticateRequestOptions['acceptsToken'] };

export interface AuthFn<TRedirect = ReturnType<typeof redirect>> {
/**
* @example
* const authObject = await auth({ acceptsToken: ['session_token', 'api_key'] })
*/
<T extends TokenType[]>(
options: AuthOptions & { acceptsToken: T },
): Promise<InferAuthObjectFromTokenArray<T, SessionAuth<TRedirect>, MachineAuth<T[number]>>>;

/**
* @example
* const authObject = await auth({ acceptsToken: 'session_token' })
*/
<T extends TokenType>(
options: AuthOptions & { acceptsToken: T },
): Promise<InferAuthObjectFromToken<T, SessionAuth<TRedirect>, MachineAuth<T>>>;

/**
* @example
* const authObject = await auth({ acceptsToken: 'any' })
*/
(options: AuthOptions & { acceptsToken: 'any' }): Promise<AuthObject>;

/**
* @example
* const authObject = await auth()
*/
(): Promise<SessionAuth<TRedirect>>;

/**
* `auth` includes a single property, the `protect()` method, which you can use in two ways:
Expand Down Expand Up @@ -68,7 +111,7 @@ export interface AuthFn {
* - Only works on the server-side, such as in Server Components, Route Handlers, and Server Actions.
* - Requires [`clerkMiddleware()`](https://clerk.com/docs/references/nextjs/clerk-middleware) to be configured.
*/
export const auth: AuthFn = (async () => {
export const auth: AuthFn = (async (options?: AuthOptions) => {
// eslint-disable-next-line @typescript-eslint/no-require-imports
require('server-only');

Expand All @@ -89,6 +132,9 @@ export const auth: AuthFn = (async () => {
const authObject = await createAsyncGetAuth({
debugLoggerName: 'auth()',
noAuthStatusMessage: authAuthHeaderMissing('auth', await stepsBasedOnSrcDirectory()),
options: {
acceptsToken: options?.acceptsToken ?? TokenType.SessionToken,
},
})(request);

const clerkUrl = getAuthKeyFromRequest(request, 'ClerkUrl');
Expand All @@ -110,8 +156,7 @@ export const auth: AuthFn = (async () => {
publishableKey: decryptedRequestData.publishableKey || PUBLISHABLE_KEY,
signInUrl: decryptedRequestData.signInUrl || SIGN_IN_URL,
signUpUrl: decryptedRequestData.signUpUrl || SIGN_UP_URL,
// TODO: Handle machine auth object
sessionStatus: authObject.tokenType === 'session_token' ? authObject.sessionStatus : null,
sessionStatus: authObject.tokenType === TokenType.SessionToken ? authObject.sessionStatus : null,
}),
returnBackUrl === null ? '' : returnBackUrl || clerkUrl?.toString(),
] as const;
Expand Down Expand Up @@ -147,6 +192,7 @@ auth.protect = async (...args: any[]) => {
redirectToSignIn: authObject.redirectToSignIn,
notFound,
redirect,
unauthorized,
});

return protect(...args);
Expand Down
Loading